I'm trying to build a single fragment(inside an activity ofcourse) application, which displays all the messages like the default messages app. There is a recyclerview in a MessageFragment. MessageRecyclerViewAdapter is the adapter for the same. I think the culprit is MessageRecyclerViewAdapter. mValues will have a map of id to sms. But the keys are being removed mValues as soon as the viewholder is detached or recycled. So, I think all the references to sms are being removed when it's removed from recycler view, but the memory keeps on increasing almost linearly as I scroll down and results in OOM. What am I missing? (I'm a backend developer, learning android for context)
class MessageRecyclerViewAdapter(
private val mListener: OnListFragmentInteractionListener?,
private val recyclerView: RecyclerView,
val context: Context
) : RecyclerView.Adapter<MessageRecyclerViewAdapter.MessageViewHolder>() {
private val mOnClickListener: View.OnClickListener
private var mValues: LinkedHashMap<Int, Sms> = LinkedHashMap()
private var mPositionToIdMap: MutableMap<Int, Int> = mutableMapOf()
private var count: Int? = null
init {
mOnClickListener = View.OnClickListener { v ->
val id = v.tag as Int
// Notify the active callbacks interface (the activity, if the fragment is attached to
// one) that an item has been selected.
mListener?.onListFragmentInteraction(mValues[id])
}
}
fun setCount(count: Int) {
this.count = count
}
override fun onCreateViewHolder(parent: ViewGroup, position: Int): MessageViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.fragment_message, parent, false)
return MessageViewHolder(view, position)
}
//This will pass position to onCreateViewHolder, instead of viewtype
override fun getItemViewType(position: Int): Int = position
override fun onBindViewHolder(holderMessage: MessageViewHolder, position: Int) {
val sms: Sms? = mValues.get(mPositionToIdMap[position])
if (sms == null) {
Log.e("MRVA", "sms == null")
return
}
val addressText = String.format("%s (%s), id:%s, %s", sms.address, DateTimeUtil.format(sms.date), sms._id, position)
Log.e("addressText", addressText)
holderMessage.mAddressView.text = addressText
with(holderMessage.mView) {
tag = sms._id
setOnClickListener(mOnClickListener)
}
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
mValues = linkedMapOf()
mPositionToIdMap = linkedMapOf()
}
override fun onViewDetachedFromWindow(holder: MessageViewHolder) {
super.onViewDetachedFromWindow(holder)
remove(holder.mPosition)
}
override fun onViewRecycled(holder: MessageViewHolder) {
super.onViewRecycled(holder)
remove(holder.mPosition)
}
fun add(
sms: Sms,
id: Int,
position: Int
) {
mValues[id] = sms
mPositionToIdMap[position] = id
}
fun remove(mPosition: Int) {
mValues.remove(mPositionToIdMap[mPosition])
mPositionToIdMap.remove(mPosition)
}
override fun getItemCount(): Int = count ?: 0
fun getId(position: Int): Int? = mPositionToIdMap[position]
fun hasData(position: Int): Boolean = mPositionToIdMap.containsKey(position)
fun getBottomMostPositionWithData(): Int {
var position = 0
mPositionToIdMap.forEach { entry ->
position = entry.key
}
return position
}
fun getTopMostPositionWithData() = mPositionToIdMap.iterator().next().key
fun get(id: Int) = mValues[id]
inner class MessageViewHolder(val mView: View, position: Int) : RecyclerView.ViewHolder(mView) {
val mAddressView: TextView = mView.address
val mPosition = position
}
}
class SmsUtil {
private final val CONTENT_URI: String = "content://sms/inbox"
fun readAllMessages(
context: Context?,
epoch: Long? = null,
limit: Int? = null,
smsHandler: SmsHandler
) {
val cursor = getCursor(context, epoch, limit) ?: return
processSms(cursor, smsHandler)
}
private fun processSms(
cursor: Cursor?,
smsHandler: SmsHandler
) {
if (cursor == null)
return
try {
if (cursor.moveToFirst()) {
do {
val sms = getSms(cursor)
Log.v(ContentValues.TAG, "SMS read $sms")
smsHandler.handle(sms)
} while (cursor.moveToNext())
} else {
Log.v(ContentValues.TAG, "The user does not have any sms")
}
}catch (e: Exception) {
} catch (e: Error) {
} finally {
cursor.close()
}
}
private fun getCursor(context: Context?, epoch: Long? = null, limit: Int? = null): Cursor? {
val selection = if (epoch != null) String.format("%s > %s", Sms.COLUMN_DATE, epoch) else null
val sortOrder =
if (limit != null) String.format("%s desc limit %s ", Sms.COLUMN_DATE, limit) else null
//above will be compiled into sql . SELECT * FROM sms_restricted WHERE (type=1) AND (_id > 100) ORDER BY limit 100
return context?.contentResolver
?.query(Uri.parse(CONTENT_URI), null, selection, null, sortOrder)
}
private fun getSms(cursor: Cursor?): Sms? {
if (cursor == null)
return null
val sms = Sms()
sms._id =
cursor.getInt(cursor.getColumnIndexOrThrow(Sms.COLUMN_ID))
sms.threadId =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_THREAD_ID))
sms.address =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_ADDRESS))
sms.person =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_PERSON))
sms.date =
cursor.getLong(cursor.getColumnIndexOrThrow(Sms.COLUMN_DATE))
sms.dateSent =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_DATE_SENT))
sms.protocol =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_PROTOCOL))
sms.read =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_READ))
sms.status =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_STATUS))
sms.type =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_TYPE))
sms.replyPathPresent =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_REPLY_PATH_PRESENT))
sms.subject =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_SUBJECT))
sms.body =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_BODY))
sms.serviceCenter =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_SERVICE_CENTER))
sms.locked =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_LOCKED))
sms.errorCode =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_ERROR_CODE))
sms.seen =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_SEEN))
sms.subId =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_SUB_ID))
sms.creator =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_CREATOR))
sms.priority =
cursor.getString(cursor.getColumnIndexOrThrow(Sms.COLUMN_PRIORITY))
return sms
}
fun getCount(context: Context?): Int {
val cursor = getCursor(context) ?: return 0
val count = cursor.count
cursor.close()
return count
}
fun getLatestMessages(
context: Context?,
epoch: Long? = null,
limit: Int? = null,
smsHandler: SmsHandler
) {
if (context == null)
return
val selection = if (epoch != null) String.format("%s > %s", Sms.COLUMN_DATE, epoch) else null
val sortOrder =
if (limit != null) String.format("%s asc limit %s ", Sms.COLUMN_DATE, limit) else null
val cursor = context.contentResolver
?.query(Uri.parse(CONTENT_URI), null, selection, null, sortOrder)
processSms(cursor, smsHandler)
}
fun getOldestMessages(
context: Context?,
epoch: Long? = null,
limit: Int? = null,
smsHandler: SmsHandler
) {
if (context == null)
return
val selection = if (epoch != null) String.format("%s < %s", Sms.COLUMN_DATE, epoch) else null
val sortOrder = String.format("%s desc limit %s ", Sms.COLUMN_DATE, limit)
val cursor = context.contentResolver
?.query(Uri.parse(CONTENT_URI), null, selection, null, sortOrder)
processSms(cursor, smsHandler)
}
}
Related
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
}
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.
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
}
I have a code to retrieve list of all music from storage but after i renamed a file the recyclerview doesnt update with the change
even after deleting a file it still remains the same
fun Context.musicFiles():MutableList{
val list:MutableList<Music> = mutableListOf()
val uri: Uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
//val uri: Uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI
// IS_MUSIC : Non-zero if the audio file is music
val selection = MediaStore.Audio.Media.IS_MUSIC + "!= 0"
// Sort the musics
val sortOrder = MediaStore.Audio.Media.TITLE + " ASC"
//val sortOrder = MediaStore.Audio.Media.TITLE + " DESC"
// Query the external storage for music files
val cursor: Cursor = this.contentResolver.query(
uri, // Uri
null, // Projection
selection, // Selection
null, // Selection arguments
sortOrder // Sort order
)
// If query result is not empty
if (cursor!= null && cursor.moveToFirst()){
val id:Int = cursor.getColumnIndex(MediaStore.Audio.Media._ID)
val title:Int = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)
val artist:Int= cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)
val musicpathh:Int= cursor.getColumnIndex(MediaStore.Audio.Media.DATA)
val album:Int = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)
// Now loop through the music files
do {
val audioId:Long = cursor.getLong(id)
val audioTitle:String = cursor.getString(title)
val audioArtist:String = cursor.getString(artist)
val audiopath:String = cursor.getString(musicpathh)
val audioalbum:String = cursor.getString(album)
// Add the current music to the list
list.add(Music(audioId,audioTitle,audioArtist,audiopath,audioalbum))
}while (cursor.moveToNext())
}
// Finally, return the music files list
return list
}
data class Music(val id:Long, val title:String, val artist:String, val audiopath:String, val audioalbumm:String)
Function to get musicfiles
val list:MutableList = musicFiles()
val titles = mutableListOf<String>()
val artist = mutableListOf<String>()
val musicpath = mutableListOf<String>()
val album = mutableListOf<String>()
val checkd = mutableListOf<Boolean>()
for (music in list){titles.add(music.title)}
for (music in list){artist.add(music.artist)}
for (music in list){musicpath.add(music.audiopath)}
for (music in list){album.add(music.audioalbumm)}
for (music in list){checkd.add(false)}
val adapter= HobbiesAdapter(this, titles, artist, musicpath, album, checkd)
recyclerView.adapter = adapter
adapter
class HobbiesAdapter(val context: Context, val hobbies: MutableList, val artis: MutableList, val pathh: MutableList, val albumm: MutableList, val checkd: MutableList) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return hobbies.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val hobby = hobbies[position]
val artistt = artis[position]
val patth = pathh[position]
val albbum = albumm[position]
holder.setData(hobby, artistt, patth, position, albbum)
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var currentHobby: String = ""
var currentArtist: String = ""
var currentPath: String = ""
var currentAlbum: String = ""
var currentPosition: Int = 0
var checkdd:Int=0
var currentCheckedStatus:Boolean=false
init {
itemView.setOnClickListener {
if(itemView.checkstatus.isChecked==true){
itemView.checkstatus.isChecked=false
checkdd=adapterPosition
checkd.set(checkdd,false)
}
else{
itemView.checkstatus.isChecked=true
checkdd=adapterPosition
checkd.set(checkdd,true)
}
}
itemView.checkstatus.setOnClickListener {
if(itemView.checkstatus.isChecked==true){
itemView.checkstatus.isChecked=false
checkdd=adapterPosition
checkd.set(checkdd,false)
}
else{
itemView.checkstatus.isChecked=true
checkdd=adapterPosition
checkd.set(checkdd,true)
}
}
}
fun setData(hobby: String, artiist: String, paath: String, pos: Int, albuum: String) {
itemView.txvTitle.text = hobby
itemView.txvArtist.text=artiist
itemView.txvPath.text=paath
itemView.txvAlbum.text=albuum
itemView.checkstatus.isChecked=checkd.get(pos)
this.currentHobby = hobby
this.currentArtist = artiist
this.currentPath = paath
this.currentAlbum = albuum
this.currentPosition = pos
this.currentCheckedStatus=checkd.get(pos)
}
}
}
I am trying to read contacts from ContactsContract in Kotin. But it's not showing any of the contact in the recyclerview. The noticeable thing is that the java version of this code is working fine.
So, I here is my Kotlin code for reading the contact:
private var adapter: ContactsAdapter? = null
private var myContacts : MutableList<MyContacts> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// code for recyclerview and adapter and checkPermission
val itemDecoration = DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.addItemDecoration(itemDecoration)
adapter = ContactsAdapter(myContacts)
recycler_view.hasFixedSize()
recycler_view.adapter = ContactsAdapter(myContacts)
checkPermission()
}
private fun loadContactFromProvider() {
showProgressBar()
val contentResolver = contentResolver
val cursor = contentResolver.query(CONTENT_URI, null, null, null, DISPLAY_NAME)
if (cursor != null && cursor.count > 0) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ID))
val name = cursor.getString(cursor.getColumnIndex(DISPLAY_NAME))
val hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(HAS_PHONE_NUMBER))
val contacts = MyContacts()
if (hasPhoneNumber > 0) {
contacts.contactName = name
val phoneCursor = contentResolver.query(PHONE_URI, arrayOf(NUMBER), "$PHONE_ID = ?", arrayOf(id), null)
val phoneNumbers = ArrayList<String>()
phoneCursor!!.moveToFirst()
while (!phoneCursor.isAfterLast) {
val phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(NUMBER)).replace(" ", "")
phoneNumbers.add(phoneNumber)
phoneCursor.moveToNext()
}
contacts.contactNumber = phoneNumbers
phoneCursor.close()
}
val inputStream = ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, ContentUris.withAppendedId(CONTENT_URI, id.toLong()))
if (inputStream != null) {
val bitmap = BitmapFactory.decodeStream(inputStream)
contacts.contactImage = bitmap?.toString()
} else {
contacts.contactImage = vectorDrawableToBitmap(R.drawable.ic_person)?.toString()
}
log(contacts.contactName + " " + contacts.contactNumber.toString() + " " + contacts.contactImage.toString())
myContacts.add(contacts)
}
adapter?.notifyDataSetChanged()
cursor.close()
}
}
The log details are also fine, they are showing the full contact list. But I am unable to see it in the recyclerview. The constant field like ID, DISPLAY_NAME etc. are already defined in the companion object.
Kotlin code for RecyclerViewAdapter is:
class ContactsAdapter(private val contactList: MutableList<MyContacts>): RecyclerView.Adapter<ContactsAdapter.ContactViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, position: Int): ContactViewHolder {
val view = LayoutInflater.from(viewGroup.context).inflate(R.layout.contact_item_layout,viewGroup,false)
return ContactViewHolder(view)
}
override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
val contact = contactList[position]
holder.name!!.text = contact.contactName
holder.mobile!!.text = contact.contactNumber[0]
Glide.with(holder.itemView.context)
.load(contact.contactImage)
.into(holder.image)
}
override fun getItemCount(): Int = contactList.size
class ContactViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
var image : ImageView = itemView.find(R.id.contact_image) as ImageView
var name : TextView? = itemView.find(R.id.contact_name) as TextView
var mobile : TextView? = itemView.find(R.id.contact_mobile) as TextView
}
}
Any help would be appreciated.
Thankyou