Troubles with nested array and JSON - android

Hello) I'm new in Android dev. And I have a problem with my program.
It's my model:
data class Test (val id: Int,
val numberQuestion: String,
val question: String,
val questionImageSrc: String,
val examination: Boolean,
val typeQuestion: String,
val singleChoiceAnswers: ArrayList<singleChoiceAnswer>,
val multipleChoiceAnswers: ArrayList<multipleChoiceAnswers>,
val inputAnswer: ArrayList<inputAnswer>)
data class multipleChoiceAnswers(val letter: String,
val text: String,
val correctAnswer: Boolean,
val checked: Boolean)
data class singleChoiceAnswer(val letter: String,
val text: String,
val correctAnswer: Boolean,
val checked: Boolean)
data class inputAnswer(val correctAnswer: String,
val userAnswer: String)
It's how i get the data from JSON:
private fun jsonResult(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<Test>()
val slist = ArrayList<singleChoiceAnswer>()
val mlist = ArrayList<multipleChoiceAnswers>()
val ilist = ArrayList<inputAnswer>()
for (i in 0 until jsonArray.length()){
val jsonObject = jsonArray.getJSONObject(i)
val typeQuestion = jsonObject.getString("typeQuestion")
val curentId = jsonObject.optInt("id")
val curentNQ = jsonObject.optString("numberQuestion")
val curentQ = jsonObject.optString("question")
val curentQIS = jsonObject.optString("questionImageSrc")
val curentEx = jsonObject.optBoolean("examination")
if (typeQuestion.contains("multipleChoice")){
val multipleChoiceAnswers = jsonObject.getJSONArray("multipleChoiceAnswers")
for (sc in 0 until multipleChoiceAnswers.length()){
val curentMCA = multipleChoiceAnswers.getJSONObject(sc)
val letter = curentMCA.optString("letter")
val text = curentMCA.optString("text")
val correctAnswer = curentMCA.optBoolean("correctAnswer")
val checked = curentMCA.optBoolean("checked")
mlist.add(multipleChoiceAnswers(letter, text, correctAnswer, checked))
}
}
if (typeQuestion.contains("singleChoice")){
val singleChoiceAnswer = jsonObject.getJSONArray("singleChoiceAnswers")
for (sc in 0 until singleChoiceAnswer.length()){
val curentSCA = singleChoiceAnswer.getJSONObject(sc)
val letter = curentSCA.optString("letter")
val text = curentSCA.optString("text")
val correctAnswer = curentSCA.optBoolean("correctAnswer")
val checked = curentSCA.optBoolean("checked")
slist.add(singleChoiceAnswer(letter, text, correctAnswer, checked))
}
}
if (typeQuestion.contains("input")){
val inputAnswer = jsonObject.getJSONArray("inputAnswer")
for (sc in 0 until inputAnswer.length()){
val curentIA = inputAnswer.getJSONObject(sc)
val correctAnswer = curentIA.optString("correctAnswer")
val userAnswer = curentIA.optString("userAnswer")
ilist.add(inputAnswer(correctAnswer,userAnswer))
}
}
list.add(Test(curentId, curentNQ, curentQ, curentQIS, curentEx, typeQuestion, slist, mlist, ilist))
}
val adapter = TestAdapter(list) { item ->
testAdapterItemClick(item)
}
val recView = findViewById<RecyclerView>(R.id.testRecyclerView)
recView.adapter = adapter
}
Here is link to my JSON. If you need it)
Then i am doing something like that:
private fun testAdapterItemClick(item: Test) {
val fT: FragmentTransaction = supportFragmentManager.beginTransaction()
val frag1: Fragment1 = Fragment1()
val frag2: Fragment2 = Fragment2()
if (item.typeQuestion == "input") {
val bundle = Bundle()
bundle.putString("NUMBER_KEY", item.numberQuestion)
bundle.putString("QUESTION_KEY", item.question)
if(!item.questionImageSrc.isNullOrEmpty())
bundle.putString("IMAGE_KEY", item.questionImageSrc)
frag1.setArguments(bundle)
fT.add(R.id.frameLayout, frag1)
}
if (item.typeQuestion == "singleChoice") {
val bundle = Bundle()
bundle.putString("NUMBER_KEY", item.numberQuestion)
bundle.putString("QUESTION_KEY", item.question)
val count = item.singleChoiceAnswers.size
Toast.makeText(this, count.toString(), Toast.LENGTH_LONG).show()
// bundle.putInt("COUNT_KEY", count)
for (i in 0 until count)
{
val curentSCA = item.singleChoiceAnswers[i]
bundle.putString("letterSCA$i", curentSCA.letter)
bundle.putString("textSCA$i", curentSCA.text)
}
frag2.setArguments(bundle)
fT.add(R.id.frameLayout, frag2)
I need to get ArrayList of the definite item and put it data in fragment using bundle.
But I have a problem in fragment:
public override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment2, container, false)
val questionNumber = rootView.findViewById(R.id.questionNumber) as TextView
val questionText = rootView.findViewById(R.id.Question) as TextView
val questionImage = rootView.findViewById(R.id.questionImage) as ImageView
val qN : String = getArguments()?.getString("NUMBER_KEY").toString()
val quest: String = getArguments()?.getString("QUESTION_KEY").toString()
val qI: String = getArguments()?.getString("IMAGE_KEY").toString()
questionNumber.text=qN
questionText.text=quest
Picasso.get().load(qI).into(questionImage)
val radioGroup = rootView.findViewById(R.id.radioGroupSetectTest) as RadioGroup
val count : Int = getArguments()!!.getInt("COUNT_KEY")
val context = getContext()
for (i in 0 until count)
{
val curentRB = RadioButton(context)
val curLetter = getArguments()?.getString("letterSCA$i")
val curText = getArguments()?.getString("textSCA$i")
curentRB.setText(curLetter+" "+curText)
radioGroup.addView(curentRB)
}
It put the all values of singleChoiseAnswer from all items like this screen. Please, help me) I know that it is a simple problem but i realy dont understand)
Thanks in advance)
P.S. SOrry for my English)

First of all, you need to convert your Json into Kotlin classes, you can use GSON
to convert your json to kotlin data classes like this
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class QuestionResponse(
#field:SerializedName("multipleChoiceAnswers")
val multipleChoiceAnswers: List<MultipleChoiceAnswersItem?>? = null,
#field:SerializedName("inputAnswer")
val inputAnswer: List<InputAnswer?>? = null,
#field:SerializedName("numberQuestion")
val numberQuestion: String? = null,
#field:SerializedName("question")
val question: String? = null,
#field:SerializedName("typeQuestion")
val typeQuestion: String? = null,
#field:SerializedName("examination")
val examination: Boolean? = null,
#field:SerializedName("id")
val id: Int? = null,
#field:SerializedName("singleChoiceAnswers")
val singleChoiceAnswers: List<MultipleChoiceAnswersItem?>? = null,
#field:SerializedName("questionImageSrc")
val questionImageSrc: String? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.createTypedArrayList(MultipleChoiceAnswersItem),
parcel.createTypedArrayList(InputAnswer),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.createTypedArrayList(MultipleChoiceAnswersItem),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeTypedList(multipleChoiceAnswers)
parcel.writeTypedList(inputAnswer)
parcel.writeString(numberQuestion)
parcel.writeString(question)
parcel.writeString(typeQuestion)
parcel.writeValue(examination)
parcel.writeValue(id)
parcel.writeTypedList(singleChoiceAnswers)
parcel.writeString(questionImageSrc)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<QuestionResponse> {
override fun createFromParcel(parcel: Parcel): QuestionResponse {
return QuestionResponse(parcel)
}
override fun newArray(size: Int): Array<QuestionResponse?> {
return arrayOfNulls(size)
}
}
}
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class MultipleChoiceAnswersItem(
#field:SerializedName("letter")
val letter: String? = null,
#field:SerializedName("checked")
val checked: Boolean? = null,
#field:SerializedName("text")
val text: String? = null,
#field:SerializedName("correctAnswer")
val correctAnswer: Boolean? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(letter)
parcel.writeValue(checked)
parcel.writeString(text)
parcel.writeValue(correctAnswer)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<MultipleChoiceAnswersItem> {
override fun createFromParcel(parcel: Parcel): MultipleChoiceAnswersItem {
return MultipleChoiceAnswersItem(parcel)
}
override fun newArray(size: Int): Array<MultipleChoiceAnswersItem?> {
return arrayOfNulls(size)
}
}
}
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
data class InputAnswer(
#field:SerializedName("correctAnswer")
val correctAnswer: String? = null,
#field:SerializedName("userAnswer")
val userAnswer: String? = null) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(correctAnswer)
parcel.writeString(userAnswer)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<InputAnswer> {
override fun createFromParcel(parcel: Parcel): InputAnswer {
return InputAnswer(parcel)
}
override fun newArray(size: Int): Array<InputAnswer?> {
return arrayOfNulls(size)
}
}
}
To know about Parcelable go to this link
Then when this is complete, parse the JSON like this
private fun jsonResult(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<QuestionResponse>()
val slist = ArrayList<MultipleChoiceAnswersItem>()
val mlist = ArrayList<MultipleChoiceAnswersItem>()
val ilist = ArrayList<InputAnswer>()
for (i in 0 until jsonArray.length()){
val jsonObject = jsonArray.getJSONObject(i)
val typeQuestion = jsonObject.getString("typeQuestion")
val curentId = jsonObject.optInt("id")
val curentNQ = jsonObject.optString("numberQuestion")
val curentQ = jsonObject.optString("question")
val curentQIS = jsonObject.optString("questionImageSrc")
val curentEx = jsonObject.optBoolean("examination")
if (typeQuestion.contains("multipleChoice")){
val multipleChoiceAnswers = jsonObject.getJSONArray("multipleChoiceAnswers")
for (sc in 0 until multipleChoiceAnswers.length()){
val curentMCA = multipleChoiceAnswers.getJSONObject(sc)
val letter = curentMCA.optString("letter")
val text = curentMCA.optString("text")
val correctAnswer = curentMCA.optBoolean("correctAnswer")
val checked = curentMCA.optBoolean("checked")
mlist.add(MultipleChoiceAnswersItem(letter,checked, text, correctAnswer))
}
}
if (typeQuestion.contains("singleChoice")){
val singleChoiceAnswer = jsonObject.getJSONArray("singleChoiceAnswers")
for (sc in 0 until singleChoiceAnswer.length()){
val curentSCA = singleChoiceAnswer.getJSONObject(sc)
val letter = curentSCA.optString("letter")
val text = curentSCA.optString("text")
val correctAnswer = curentSCA.optBoolean("correctAnswer")
val checked = curentSCA.optBoolean("checked")
slist.add(MultipleChoiceAnswersItem(letter, checked,text, correctAnswer))
}
}
if (typeQuestion.contains("input")){
val inputAnswer = jsonObject.getJSONArray("inputAnswer")
for (sc in 0 until inputAnswer.length()){
val curentIA = inputAnswer.getJSONObject(sc)
val correctAnswer = curentIA.optString("correctAnswer")
val userAnswer = curentIA.optString("userAnswer")
ilist.add(InputAnswer(correctAnswer,userAnswer))
}
}
val questionResponse = QuestionResponse(mlist,ilist,curentNQ,curentQ,typeQuestion,curentEx,curentId,slist,curentQIS)
//pass this questionResponse to your adapter
}
}
Then in Adapter Item click
private fun testAdapterItemClick(item: Test) {
val fT: FragmentTransaction = supportFragmentManager.beginTransaction()
val frag1: Fragment1 = Fragment1()
val frag2: Fragment2 = Fragment2()
if (item.typeQuestion == "input") {
val bundle = Bundle()
bundle.putParcelable("input", item)
frag1.setArguments(bundle)
fT.add(R.id.frameLayout, frag1)
}
// do the same for the rest
}
Then in the Fragment
public override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val rootView = inflater.inflate(R.layout.fragment2, container, false)
val questionNumber = rootView.findViewById(R.id.questionNumber) as TextView
val questionText = rootView.findViewById(R.id.Question) as TextView
val questionImage = rootView.findViewById(R.id.questionImage) as ImageView
val item = arguments.getParcelable("input")
// val arraylist = arguments.getParcelableArrayList<YOUR_CLASS_TYPE>("key") //for arrays you can do it like this
//then set it in the radiobutton like this item.getCurrentText and so on
}

Related

How to get Folder size and number of Files avaliable from Folder path in Kotlin

i am trying to create a video player app using kotlin , First of all I got the videos files by using MediaStore , than store this in ArrayList so far it's been perfect but When I made a folder list of videos, I tried to find out the size of those folders and how many video files there are in those folders, but I failed. like this (Image)
Check this image for more clear
This is my data class code (VideoItem.Kt)
import android.net.Uri
data class VideoItem(
val id: String,
val title: String,
val duration: Long = 0,
val folderName: String,
val size: String,
val path: String,
val dateAdded: String,
val artUri: Uri
)
data class FolderItem(
val id: String,
val folderName: String,
val folderSize: Long
)
This is my MainActivity Code To get Allvideos Using MediaStore
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
companion object {
lateinit var videoList: ArrayList<VideoItem>
lateinit var folderList: ArrayList<FolderItem>
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
folderList = ArrayList()
videoList = getAllVideos()
setFragment(VideoviewFragment())
}
private fun setFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.FrameLayout, fragment)
transaction.disallowAddToBackStack()
transaction.commit()
}
#SuppressLint("Recycle", "Range")
private fun getAllVideos(): ArrayList<VideoItem> {
val tempList = ArrayList<VideoItem>()
val tempFolderList = ArrayList<String>()
val projection = arrayOf(
MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media._ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.BUCKET_ID
)
val cursor = this.contentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
MediaStore.Video.Media.DATE_ADDED + " DESC"
)
if (cursor != null)
if (cursor.moveToNext())
do {
val titleC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE))
val idC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media._ID))
val folderNameC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME))
val folderIdC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID))
val sizeC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
val pathC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA))
val dateAddedC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED))
val durationC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
.toLong()
try {
val file = File(pathC)
val artUriC = Uri.fromFile(file)
val video = VideoItem(
title = titleC,
id = idC,
folderName = folderNameC,
size = sizeC,
path = pathC,
duration = durationC,
dateAdded = dateAddedC,
artUri = artUriC
)
if (file.exists()) tempList.add(video)
//for adding Folders
if (!tempFolderList.contains(folderNameC)) {
tempFolderList.add(folderNameC)
val folderSizeC = getFileLength(pathC)
folderList.add(
FolderItem(
id = folderIdC,
folderName = folderNameC,
folderSize = folderSizeC
)
)
}
} catch (_: Exception) {
}
} while (cursor.moveToNext())
cursor?.close()
return tempList
}
private fun getFileLength(path: String?): Long {
return if (!isExistFile(path)) 0 else File(path.toString()).length()
}
private fun isExistFile(path: String?): Boolean {
val file = File(path.toString())
return file.exists()
}
}
This is my RecyclerviwAdapter Code(FolderAdapter.kt)
class FoldersAdapter(private val context: Context, private var foldersList: ArrayList<FolderItem>) :
RecyclerView.Adapter<FoldersAdapter.MyHolder>() {
class MyHolder(binding: FolderItemBinding) : RecyclerView.ViewHolder(binding.root) {
val folderName = binding.folderName
val noofFiles = binding.nooffiles
val folderSize = binding.foldersize
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
return MyHolder(FolderItemBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
holder.folderName.text = foldersList[position].folderName
val size: Long = foldersList[position].folderSize
holder.folderSize.text = android.text.format.Formatter.formatFileSize(context, (size))
holder.root.setOnClickListener {
val intent = Intent(context, FolderVideosActivity::class.java)
intent.putExtra("position", position)
ContextCompat.startActivity(context, intent, null)
}
}
override fun getItemCount(): Int {
return foldersList.size
}
}
This is my all codes now please check out all code and suggest the best.
Thank you
Use this function for size
private fun getFolderSize(f: File): Long {
var size: Long = 0
if (f.isDirectory) {
for (file in f.listFiles()!!) {
size += getFolderSize(file)
}
} else {
size = f.length()
}
return size
}
And Count number of files Use this
val length = File("/path/to/folder").listFiles()?.size

kotlin.Unit cannot be cast to java.util.List. for Custom Adapter on getFilter

I am using a custom adapter to display a list of contacts on AutoTextComplete but when I try to run it I get the error "kotlin.Unit cannot be cast to java.util.List" for
mContact = filterResults.values **as List<Contact>**
Contact is a Serializable object and is how i am retrieving contact names as objects in the List - code is below... I have tried looking for a solution elsewhere, but have not been able to resolve... if you can redirect or solution or guide will be amazing thanks
private var invoice: Invoice? = null
private lateinit var contact: Contact
private var invoiceItems: List<InvoiceItems>? = null
private lateinit var contactSelect: ArrayList<Contact>
private lateinit var dueDate: Calendar
private val calendar = Calendar.getInstance()
private var total = 0
private var subTotal = 0
private var taxrate = 0
private var invoiceType: String = ""
private var invoiceUpdt: InvoiceItems? = null
private var j: String? = null
private var clientLkey: String? = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_invoice)
val toolbar: Toolbar = findViewById(R.id.toolbar_editinv)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowHomeEnabled(true)
val invoiceClient = findViewById<AutoCompleteTextView>(R.id.invoiceClient)
val invoiceDueDt = findViewById<TextView>(R.id.invoiceDueDt)
dueDate = Calendar.getInstance()
//getting values from intent
invoice = intent.getSerializableExtra("invoice") as? Invoice
invoiceItems = intent.getSerializableExtra("invoiceItem") as? List<InvoiceItems>
invoiceUpdt = intent.getSerializableExtra("invoiceItemUpdt") as? InvoiceItems
j = intent.getStringExtra("i")
if (invoice == null){
invoiceType = "new"
supportActionBar?.title = "Add Invoice"
addinvoiceItem()
} else {
editInvoice()
}
//Setup Due date for the invoice
invoiceDueDt.setOnClickListener {
showCalendar()
}
//Auto complete based on database for selecting the client
val clientContact: List<Contact> = ArrayList<Contact>()
val adapter = ClientSelectAdapter(this, R.layout.userlatomcontacts, clientContact)
invoiceClient.setAdapter(adapter)
invoiceClient.threshold = 2
invoiceClient.setOnItemClickListener { parent, _, position, id ->
val selectedClient = parent.adapter.getItem(position) as Contact?
invoiceClient.setText(selectedClient?.name)
clientLkey = selectedClient?.lookupKey
}
}
inner class ClientSelectAdapter(
context: Context,
#LayoutRes private val layoutResource: Int,
private var allContacts: List<Contact>
):
ArrayAdapter<Contact>(context, layoutResource, allContacts),
Filterable {private var mContact: List<Contact> = allContacts
override fun getCount(): Int {
return mContact.size
}
override fun getItem(p0: Int): Contact {
return mContact[p0]
}
override fun getItemId(p0: Int): Long {
// Or just return p0
return mContact[p0].id.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view = convertView
if (view == null) {
view = LayoutInflater.from(parent.context)
.inflate(layoutResource, parent, false)
}
val invoiceClient = view!!.findViewById<View>(R.id.invoiceClient) as TextView
invoiceClient.text = mContact[position].name
val clientProfile = view.findViewById<View>(R.id.profile_image) as ShapeableImageView
Picasso.get().load(mContact[position].photoUri)
.placeholder(R.drawable.ic_baseline_whatshot_24).fit().centerCrop()
.into(clientProfile)
return view
}
override fun getFilter(): Filter {
return object : Filter() {
override fun publishResults(
charSequence: CharSequence?,
filterResults: FilterResults
) {
**mContact = filterResults.values as List<Contact>**
notifyDataSetChanged()
}
override fun performFiltering(charSequence: CharSequence?): FilterResults {
val queryString = charSequence?.toString()?.toLowerCase(Locale.ROOT)
val results = FilterResults()
results.values = if (queryString == null || queryString.isEmpty())
allContacts
else {
val db = AppDatabase.getDatabase(context)
allContacts = db.contactsDao().getBySearch(queryString)
}
return results
}
}
}
}
According to kotlin documentations Branches of if branches can be blocks. In this case, the last expression is the value of a block
So, in your case, when code enters the else block, the last expression is assignment, and assignments return Unit in kotlin. You may check this situation with this simple example:
var temp = 0
var result: Int = if (temp > 0) {
5
} else {
temp = 5
}
You would see the ide error at temp = 5 since it returns Unit whereas result expects Int.
To sum up, you just need to change your else block as follows:
else {
val db = AppDatabase.getDatabase(context)
allContacts = db.contactsDao().getBySearch(queryString)
allContacts
}

List size + position too large, last item in list beyond totalCount

I'm trying to get device contacts using paging library (3.0.0-alpha02 version). But when I try to invalidate the data source I get this exception:
java.lang.IllegalArgumentException: List size + position too large, last item in list beyond totalCount.
Here's my code.
Data Class:
data class Contact(
val id: Long,
val lookupUri: Uri,
val name: String?,
val photoUri: Uri?
)
DataSource:
class ContactsDataSource(private val contentResolver: ContentResolver) : PositionalDataSource<Contact>() {
override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<Contact>) {
val totalSize = getRowsNumbers()
val firstLoadPosition = computeInitialLoadPosition(params, totalSize)
val firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalSize)
val list = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
CONTACT_PROJECTION,
null,
null,
"${CONTACT_PROJECTION[2]} ASC LIMIT $firstLoadSize OFFSET $firstLoadPosition"
)?.use {
getRows(it)
}!!
callback.onResult(list, firstLoadPosition, firstLoadSize)
}
override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<Contact>) {
val list = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
CONTACT_PROJECTION,
null,
null,
"${CONTACT_PROJECTION[2]} ASC LIMIT ${params.loadSize} OFFSET ${params.startPosition}"
)?.use {
getRows(it)
}!!
callback.onResult(list)
}
private fun getRowsNumbers() = contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
arrayOf(BaseColumns._ID),
null,
null,
null
)?.use {
it.count
} ?: 0
private fun getRows(cursor: Cursor) = arrayListOf<Contact>().apply {
cursor.apply {
while (moveToNext()) {
val id = getLong(getColumnIndex(CONTACT_PROJECTION[0]))
add(
Contact(
id,
ContactsContract.Contacts.getLookupUri(
id,
getString(getColumnIndex(CONTACT_PROJECTION[1]))
),
getString(getColumnIndex(CONTACT_PROJECTION[2])),
getString(getColumnIndex(CONTACT_PROJECTION[3]))?.toUri()
)
)
}
}
}.toList()
companion object {
private val CONTACT_PROJECTION = arrayOf(
ContactsContract.Contacts._ID,
ContactsContract.Contacts.LOOKUP_KEY,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_URI
)
}
}
DataSourceFactory:
class ContactsDataSourceFactory(private val contentResolver: ContentResolver) :
DataSource.Factory<Int, Contact>() {
override fun create(): DataSource<Int, Contact> {
return ContactsDataSource(contentResolver)
}
}
ViewModel:
class ContactsViewModel(app: Application) : AndroidViewModel(app) {
lateinit var currentDataSource: PagingSource<Int, Contact>
val contacts = Pager(
PagingConfig(
pageSize = 20
)
) {
ContactsDataSourceFactory(app.contentResolver).asPagingSourceFactory().invoke().also {
currentDataSource = it
}
}
.flow
.cachedIn(viewModelScope)
}
RecyclerView Adapter:
private val CONTACT_DIFF_UTIL = object : DiffUtil.ItemCallback<Contact>() {
override fun areItemsTheSame(oldItem: Contact, newItem: Contact): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Contact, newItem: Contact): Boolean =
oldItem == newItem
}
class ContactsRvAdapter(private val onLongClick: () -> Unit) :
PagingDataAdapter<Contact, ContactsRvAdapter.Holder>(CONTACT_DIFF_UTIL) {
inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val ivContactPhoto = itemView.findViewById<ImageView>(R.id.ivContactPhoto)
private val tvContactName = itemView.findViewById<TextView>(R.id.tvContactName)
fun bind(contact: Contact?) {
contact ?: return
val photoUri = contact.photoUri
if (photoUri != null) {
ivContactPhoto.setImageURI(photoUri)
} else {
ivContactPhoto.setImageResource(R.drawable.ic_baseline_account_circle_60)
}
tvContactName.text = contact.name
itemView.setOnLongClickListener {
onLongClick()
return#setOnLongClickListener true
}
}
}
override fun onBindViewHolder(holder: Holder, position: Int) {
holder.bind(getItem(position))
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.contacts_rv_row_item, parent, false)
return Holder(view)
}
}
Activity:
#ExperimentalCoroutinesApi
class MainActivity : AppCompatActivity() {
private val contactsViewModel: ContactsViewModel by viewModels()
private val contactsRvAdapter = ContactsRvAdapter {
contactsViewModel.currentDataSource.invalidate()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<RecyclerView>(R.id.rvContacts).apply {
adapter = contactsRvAdapter
addItemDecoration(
DividerItemDecoration(
this#MainActivity,
DividerItemDecoration.VERTICAL
)
)
}
lifecycleScope.launch {
contactsViewModel.contacts.collectLatest {
contactsRvAdapter.submitData(it)
}
}
}
}
I read generated code by room database and source code of LimitOffsetDataSource class and tried to follow the exact steps to build my own data source. Am I missing something? Any help or solution is appreciated.

ExpandedListView: how to expandGroup or get correct groupPosition with List<Class>

Before this problem i could expand groups.
Group and child collection was:
val header: MutableList<String> = ArrayList()
var productList : MutableList<String> = mutableListOf()
val body: MutableList<MutableList<String>> = mutableListOf()
After that I needed to add more data to my header and childs (productList). So i created two model classes:
data class Cell (var barcode: String = "", var name: String = "")
data class Product(
var barcode: String = "",
var id: String = "",
var name: String = "",
var time: String = "",
var date: String = "",
var weight: Int = 0
)
And changed my collections in Activity to:
var header: MutableList<Cell> = mutableListOf()
var productList : MutableList<Product> = mutableListOf()
val body: MutableList<MutableList<Product>> = mutableListOf()
My adapter is:
class ExpandableListAdapter(var context : Context, var expandableListView: ExpandableListView, var header : MutableList<Cell>, var body: MutableList<MutableList<Product>>) :
BaseExpandableListAdapter() {
override fun getGroup(groupPosition: Int): String {
return header[groupPosition].toString()
}
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return true
}
override fun hasStableIds(): Boolean {
return false
}
override fun getGroupView(
groupPosition: Int,
isExpanded: Boolean,
convertView: View?,
parent: ViewGroup?
): View? {
var convertView = convertView
if(convertView == null){
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = inflater.inflate(R.layout.layout_group, null)
}
val title = convertView?.findViewById<TextView>(R.id.textGroupCode)
title?.text = getGroup(groupPosition)
val det = header.get(groupPosition)
title?.setText(det.name.toString())
title?.setOnClickListener {
if (expandableListView.isGroupExpanded(groupPosition))
expandableListView.collapseGroup(groupPosition)
else
expandableListView.expandGroup(groupPosition)
Toast.makeText(context, getGroup(groupPosition).toString(), Toast.LENGTH_SHORT).show()
}
return convertView
}
override fun getChildrenCount(groupPosition: Int): Int {
return body[groupPosition].size
//return header.get(groupPosition).getChildList().size()
}
override fun getChild(groupPosition: Int, childPosition: Int): String {
return body[groupPosition][childPosition].toString()
//return header.get(groupPosition).get(childPosition)
}
override fun getGroupId(groupPosition: Int): Long {
return groupPosition.toLong()
}
override fun getChildView(
groupPosition: Int,
childPosition: Int,
isLastChild: Boolean,
convertView: View?,
parent: ViewGroup?
): View? {
var convertView = convertView
if(convertView == null){
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
convertView = inflater.inflate(R.layout.layout_child, null)
}
val title = convertView?.findViewById<TextView>(R.id.textChildId)
title?.text = getChild(groupPosition, childPosition)
val name = convertView?.findViewById<TextView>(R.id.textChildName)
val date = convertView?.findViewById<TextView>(R.id.textChildDate)
val time = convertView?.findViewById<TextView>(R.id.textChildTime)
val weight = convertView?.findViewById<TextView>(R.id.textChildWeight)
val det = body.get(groupPosition).get(childPosition)
title?.setText(det.id.toString())
name?.setText(det.name.toString())
date?.setText(det.date.toString())
time?.setText(det.time.toString())
weight?.setText(det.weight.toString())
title?.setOnClickListener {
Toast.makeText(context, getChild(groupPosition, childPosition), Toast.LENGTH_SHORT).show()
}
return convertView
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return childPosition.toLong()
}
override fun getGroupCount(): Int {
return header.size
}
}
Now when i try to expand/click on my group or child, i get class with his all field values. I dont get position of element. How to fix this? How to work with List of class and get correct position???
Please try this one, i have print group position, child position and handle expend event.
class ExpendNewListAdapter(
var mContext: Context,
var mListDataHeader: List<String>?,
var mListDataChild: HashMap<String, List<EventsItem>?>?) : BaseExpandableListAdapter() {
override fun getChild(groupPosition: Int, childPosititon: Int): Any {
return this.mListDataChild?.get(this.mListDataHeader?.get(groupPosition))!!.get(childPosititon)
}
override fun getChildId(groupPosition: Int, childPosition: Int): Long {
return childPosition.toLong()
}
override fun getChildView(groupPosition: Int, childPosition: Int,
isLastChild: Boolean, convertView: View?, parent: ViewGroup): View {
val events = getChild(groupPosition, childPosition) as EventsItem
val infalInflater = this.mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val childView = infalInflater.inflate(R.layout.list_item_expandable, null)
val txtListEventName = childView
.findViewById<View>(R.id.text_event_name) as TextView
Log.e("child Position",childPosition.toString())
return childView
}
override fun getChildrenCount(groupPosition: Int): Int {
return this.mListDataChild?.get(this.mListDataHeader?.get(groupPosition))?.size ?:0
}
override fun getGroup(groupPosition: Int): String? {
return this.mListDataHeader?.get(groupPosition)
}
override fun getGroupCount(): Int {
return this.mListDataHeader?.size?:0
}
override fun getGroupId(groupPosition: Int): Long {
return groupPosition.toLong()
}
override fun getGroupView(groupPosition: Int, isExpanded: Boolean,
convertView: View?, parent: ViewGroup): View {
val infalInflater = this.mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val headerView = infalInflater.inflate(R.layout.expend_list_layout, null)
val innerLayout = headerView
.findViewById<View>(R.id.layout_inner) as ConstraintLayout
val viewBottom = headerView
.findViewById<View>(R.id.view_botttom_parent) as View
Log.e("group Position",groupPosition.toString())
if(isExpanded ) {
innerLayout.setBackgroundColor(ContextCompat.getColor(mContext, R.color.button_green))
viewBottom.visibility = View.GONE
}
else
{
innerLayout.setBackgroundColor(ContextCompat.getColor(mContext,R.color.button_blue))
viewBottom.visibility=View.VISIBLE
}
return headerView
}
override fun hasStableIds(): Boolean {
return false
}
override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
return true
}
}
In my case i have set all events month wise, where group items are month and child items are all events which are getting from my response.you can change as per your requirement.
if (dataList != null && dataList.size > 0) {
text_empty.visibility = View.GONE
mListDataHeader = ArrayList()
mListDataChild = HashMap()
// Adding the header of parent.because month are common for each year then put static data
mListDataHeader?.add(getString(R.string.january))
mListDataHeader?.add(getString(R.string.feburary))
mListDataHeader?.add(getString(R.string.march))
mListDataHeader?.add(getString(R.string.april))
mListDataHeader?.add(getString(R.string.may))
mListDataHeader?.add(getString(R.string.june))
mListDataHeader?.add(getString(R.string.july))
mListDataHeader?.add(getString(R.string.august))
mListDataHeader?.add(getString(R.string.sep))
mListDataHeader?.add(getString(R.string.oct))
mListDataHeader?.add(getString(R.string.nov))
mListDataHeader?.add(getString(R.string.dec))
mIsRequestForAccess?.let {
if (!it) {
mListDataHeader?.add("") /* these blank item to create space in bottom for
scrolling the list up to request for access view*/
mListDataHeader?.add("")
}
}
/* In this List First we are check the list of event item is empty or not
* if it is empty then we add a blank item for "No Event Found".if the list is not blank then
* we check start and end date are common or not if it is comman then we create a new list and add a event in this list
* and if the date are not common then we get all dates between start and end date..and create a different events
* of those dates..(events name and title are common between start and end date so we change only start date of
* all event which are belong from those dates)..we show the child events for base on start dates.so we change only start date*/
for (item in dataList.indices) {
mListDataChild?.let {
if (dataList[item]?.events?.size == 0) {
var list: List<EventsItem>? = null
list = ArrayList()
val events = EventsItem(null, null, null, getString(R.string.not_events_found), null)
list.add(events)
it.put(mListDataHeader!!.get(item), list)
} else {
var newList: List<EventsItem>? = null
newList = ArrayList()
val events = dataList[item]?.events as List<EventsItem>?
val monthDate=dataList[item]?.date
events?.let {
for (i in it.indices) {
val event = it[i]
val startTime = event.startDate
val endTime = event.endDate
val startDate=Utils.getFormatedTimeFromUtc(startTime,getString(R.string.date_format_data))
val endDate=Utils.getFormatedTimeFromUtc(endTime,getString(R.string.date_format_data))
val colors = event.color
val description = event.description
val title = event.title
if (startDate.equals(endDate)) {
newList.add(event)
} else {
/* get all dates between start and end date*/
var startnewDate: String? = null
val mLists = Utils.getDates(startDate, endDate, activity)
val newFormate = SimpleDateFormat(getString(R.string.date_format_data), Locale.getDefault())
val format = SimpleDateFormat(getString(R.string.date_month_formats), Locale.getDefault())
var mDateStart: Date? = null
try {
mDateStart = newFormate.parse(startDate)
startnewDate = format.format(mDateStart)
} catch (e: ParseException) {
e.printStackTrace()
}
mLists.let {
for (data in it.indices) {
val dateFormat = SimpleDateFormat(getString(R.string.date_format_data), Locale.getDefault())
val datenew = format.format(it[data])
if (datenew == monthDate) {
val newData = dateFormat.format(it[data])
val newEvent = EventsItem(endDate, colors, description, title, newData)
newList.add(newEvent)
}
}
}
}
}
}
val sortedList = newList.sortedWith(compareBy({ it.startDate }))
it[mListDataHeader!!.get(item)] = sortedList as List<EventsItem>?
}
}
setData()
}
} else {
text_empty.visibility = View.VISIBLE
}

Can't update object in Room Database

I want to update my object in Room by id, but it doesn't work.
Here is my DAO class
#Query("UPDATE greeting SET id =:id")
abstract fun updateGreetingByID(id: String)
Here is my DBHelper
fun updateGreetingByID(id: String) {
Thread { dataBase.greetingDao().updateGreetingByID(id) }.start()
}
My model
#Entity(tableName = "greeting")
class GreetingModel(
id: String? = "",
greetingCategory: String = "",
isFavourite: Boolean = false,
position: Int = 0
) : Parcelable {
#PrimaryKey(autoGenerate = true)
var _id: Int = 0
#ColumnInfo(name = "id")
var id: String? = null
#ColumnInfo(name = "greetingCategory")
var greetingCategory: String? = null
#ColumnInfo(name = "isFavourite")
var isFavourite: Boolean? = null
#ColumnInfo(name = "imageId")
var imageId: ByteArray? = null
#ColumnInfo(name = "position")
var position: Int = 0
#ColumnInfo(name = "saved")
var saved: Int = 0
constructor(parcel: Parcel) : this(
) {
_id = parcel.readInt()
id = parcel.readString()
greetingCategory = parcel.readString()
isFavourite = parcel.readValue(Boolean::class.java.classLoader) as? Boolean
imageId = parcel.createByteArray()
position = parcel.readInt()
saved = parcel.readInt()
}
init {
this.id = id
this.greetingCategory = greetingCategory
if (position != null) {
this.position = position
}
this.isFavourite = isFavourite
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(_id)
parcel.writeString(id)
parcel.writeString(greetingCategory)
parcel.writeValue(isFavourite)
parcel.writeByteArray(imageId)
parcel.writeInt(position)
parcel.writeInt(saved)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GreetingModel> {
override fun createFromParcel(parcel: Parcel): GreetingModel {
return GreetingModel(parcel)
}
override fun newArray(size: Int): Array<GreetingModel?> {
return arrayOfNulls(size)
}
}
}
Where id is val id = UUID.randomUUID().toString()
I want to update object when user click to favourite button
val greetingModel = GreetingModel()
greetingModel.isFavourite = true
greetingModel.greetingCategory = " "
greetingModel.position = postImage
greetingModel.id?.let { helper.updateGreetingByID(greetingModel.id!!) }
My update method doesn't work. Hope, you will help!
Your issue is that you are trying to update according to a newly constructed Greeting that has the id as whatever value the id is set to when the Greeting is constructed using the default (no parameters constructor).
The fix is to retrieve the specific id of the Greeting that was clicked and set the id to that value before doing the update.

Categories

Resources