Recycler view not updating properly - android

I have asked this similar questions multiple times and its been more than 2 weeks that I am trying to figure out a solution for it. Yes, I am aware that there are multiple similar question but the reason why I am asking is because none of the solutions from stack over flow work.I am a beginner in Android development, hence I ask you to kindly help me with this problem. I have almost given up on my project.
How app works
App has two activities. Main activity is used for showing data in form of card views using recycler view which is loaded from SQ Lite DB. User can click on a card which on clicking will launch a new activity(REFERENCE ACTIVITY). Data of clicked card is loaded in second activity. Here user can perform UPDATE or DELETE function.
PROBLEM
The problem is that I have to restart my app just to see a change in a note. For example, if I create a note in second activity and return to main activity, that CREATED note is NOT visible until app is restarted. Same goes for when a note is updated or deleted. Change only occurs when app is restarted.
What I have tried so far...
called notifyDataSetChanged() in onRestart() of MAIN activity.
Recalled whole load that loads recycler view items in first place in onRestart()
Tried updating adapter with the new data
----- CODES -----
MAIN ACTIVITY
class MainActivity : AppCompatActivity() {
var dbHandler: PediaDatabase = PediaDatabase(this)
var adapter: PediaAdapter? = null
var newAdapter: PediaAdapter? = null
var layoutManager: RecyclerView.LayoutManager? = null
var list: ArrayList<UserNotes>? = ArrayList()
var listItems: ArrayList<UserNotes>? = ArrayList()
var updatedList: ArrayList<UserNotes> = ArrayList()
var tempList: ArrayList<UserNotes>? = null
var myPrefs: SharedPreferences? = null
var first_run: Boolean = true
var isCreated: Boolean = false
var isUpdated: Boolean = false
var isDeleted: Boolean = false
var isReturning: Boolean = false
var updatedTitle: String? = null
var updatedText: String? = null
val PREFS_NAME: String = "MYPREFS"
val REQUEST_CODE: Int = 1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
showOneTimeMessage()
invalidateOptionsMenu()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
window.navigationBarColor = Color.BLACK
}
}
override fun onRestart() {
super.onRestart()
if(isUpdated)
{
recyclerViewID.recycledViewPool.clear()
var test = listItems!![adapPos]
test.noteTitle = updatedTitle
test.noteText = updatedText
listItems!!.set(adapPos, test)
adapter!!.notifyItemChanged(adapPos)
isUpdated = false
}
}
override fun onStart() {
super.onStart()
if(isReturning)
{
adapter!!.updateResults(this.listItems!!)
adapter!!.notifyDataSetChanged()
}
adapter = PediaAdapter(this, listItems!!)
layoutManager = LinearLayoutManager(this)
recyclerViewID.adapter = adapter
recyclerViewID.layoutManager = layoutManager
list = dbHandler.readAllNotes()
for(reader in list!!.iterator())
{
var note = UserNotes()
note.noteTitle = reader.noteTitle
note.noteText = reader.noteText
note.noteID = reader.noteID
note.noteDate = reader.noteDate
listItems!!.add(note)
}
adapter!!.notifyDataSetChanged()
if(dbHandler.totalNotes() == 0) {
recyclerViewID.visibility = View.GONE
}
else{
recyclerViewID.visibility = View.VISIBLE
showWhenEmptyID.visibility = View.GONE
}
recyclerViewID.hasFixedSize()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == REQUEST_CODE)
if(resultCode == Activity.RESULT_OK)
{
//isCreated = data!!.extras!!.getBoolean("isCreated")
updatedTitle = data!!.extras.getString("updatedTitle")
updatedText = data!!.extras.getString("updatedText")
isReturning = data.extras!!.getBoolean("returningBack")
// isUpdated = data.extras!!.getBoolean("isUpdated")
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.top_menu, menu)
val item = menu!!.findItem(R.id.delete_note_menu)
item.setVisible(false)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if(item!!.itemId == R.id.add_note_menu){
isNewNote = true
startActivity(Intent(this, ReferenceActivity::class.java))
}
if(item!!.itemId == R.id.delete_note_menu)
{
Toast.makeText(this,"DELETED", Toast.LENGTH_SHORT).show()
}
return super.onOptionsItemSelected(item)
}
private fun showOneTimeMessage()
{
var data: SharedPreferences = getSharedPreferences(PREFS_NAME, 0)
if(data.contains("isShown"))
{
first_run = data.getBoolean("isShown", true)
}
Log.d("FIRST_RUN", first_run.toString())
if(first_run)
{
val oneTimeMsg = SweetAlertDialog(this)
oneTimeMsg.setTitleText("Hey there!")
oneTimeMsg.setContentText("Thank you for downloading! Please don`t forget to rate our app :)").show()
oneTimeMsg.setConfirmClickListener(object : SweetAlertDialog.OnSweetClickListener
{
override fun onClick(sweetAlertDialog: SweetAlertDialog?)
{
oneTimeMsg.dismissWithAnimation()
}
}).show()
myPrefs = getSharedPreferences(PREFS_NAME, 0)
var editor: SharedPreferences.Editor = (myPrefs as SharedPreferences).edit()
editor.putBoolean("isShown", false)
editor.commit()
}
}
REFERENCE ACTIVITY
class ReferenceActivity : AppCompatActivity() {
var dbHandler: PediaDatabase? = null
var note = UserNotes()
var noteExisted: Boolean = false
var cardAdapterPos: Int? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reference)
getSupportActionBar()!!.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM)
getSupportActionBar()!!.setCustomView(R.layout.custom_toolbar)
val dateTxtView = findViewById<View>(resources.getIdentifier("action_bar_title", "id", packageName)) as TextView
overridePendingTransition(R.anim.slide_in, R.anim.slide_out)
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
window.navigationBarColor = Color.BLACK
dbHandler = PediaDatabase(this)
val data = intent
if(isNewNote != true/*isNewNote.extras.getBoolean("isNewNote") != true*/)
{
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
if(data != null)
{
noteExisted = true
this.cardAdapterPos = data.extras.getInt("cardPosition")
cardID = data.extras.getInt("cardID")
existingNote = dbHandler!!.readNote(cardID)
refTitleID.setText(existingNote.noteTitle, TextView.BufferType.EDITABLE)
refTextID.setText(existingNote.noteText, TextView.BufferType.EDITABLE)
dateTxtView.text = existingNote.noteDate.toString()
}
}else{
dateTxtView.text = "New note"
}
}
override fun onStop() {
super.onStop()
var title: String = refTitleID.text.toString().trim()
var text: String = refTextID.text.toString().trim()
if(existingNote.noteText == text && existingNote.noteTitle == title)
finish()
if(noteExisted)
{
if(TextUtils.isEmpty(title))
title = "No title"
existingNote.noteTitle = title
existingNote.noteText = text
existingNote.noteDate = System.currentTimeMillis().toString() //TODO TRY THIS
dbHandler!!.updateNote(existingNote)
var dataCreate = this.intent
dataCreate.putExtra("isUpdated", true)
dataCreate.putExtra("updatedTitle", title)
dataCreate.putExtra("updatedText",text)
dataCreate.putExtra("returningBack", true)
setResult(Activity.RESULT_OK, dataCreate)
finish()
}
else
{
if(TextUtils.isEmpty(title) && TextUtils.isEmpty(text))
{
finish()
}
else
{
if(TextUtils.isEmpty(title))
title = "No title"
note.noteTitle = title
note.noteText = text
note.noteDate = System.currentTimeMillis().toString()
// note.noteID =
dbHandler!!.createNote(note)
var dataCreate = this.intent
dataCreate.putExtra("isCreated", true)
dataCreate.putExtra("returningBack", true)
setResult(Activity.RESULT_OK, dataCreate)
finish()
}
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.top_menu, menu)
val addItem: MenuItem = menu!!.findItem(R.id.add_note_menu)
val delItem:MenuItem = menu.findItem(R.id.delete_note_menu)
addItem.setVisible(false)
delItem.setVisible(false)
if(noteExisted)
delItem.setVisible(true)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
if(item!!.itemId == R.id.delete_note_menu)
{
val dialogMsg = SweetAlertDialog(this, SweetAlertDialog.WARNING_TYPE)
dialogMsg.setTitleText("Are you sure?")
dialogMsg.setContentText("You won`t be able to recover this note!")
dialogMsg.setConfirmText("Yes, delete it!")
dialogMsg.setConfirmClickListener(object: SweetAlertDialog.OnSweetClickListener {
override fun onClick(sweetAlertDialog: SweetAlertDialog?) {
dialogMsg.dismissWithAnimation()
dbHandler!!.deleteNote(cardID)
val successMsg = SweetAlertDialog(sweetAlertDialog!!.context, SweetAlertDialog.SUCCESS_TYPE)
successMsg.setTitleText("Note deleted!")
successMsg.setContentText("So long,note").show()
successMsg.setCancelable(false)
//TODO Disable 'OK' button on successMsg dialogbox
Handler().postDelayed({
successMsg.dismissWithAnimation()
finish()
}, 1200)
}
}).show()
}
return super.onOptionsItemSelected(item)
}
ADAPTER
class PediaAdapter(private val context: Context,
private var noteslist: ArrayList<UserNotes>): RecyclerView.Adapter<PediaAdapter.ViewHolder>() {
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder
{
val view = LayoutInflater.from(context).inflate(R.layout.list_row,p0,false)
return ViewHolder(view, noteslist)
}
override fun getItemCount(): Int
{
return noteslist.size
}
override fun onBindViewHolder(p0: ViewHolder, p1: Int)
{
p0.bindItems(noteslist[p1])
}
inner class ViewHolder(itemView: View, list: ArrayList<UserNotes>): RecyclerView.ViewHolder(itemView), View.OnClickListener
{
private var noteTitle = itemView.findViewById(R.id.listTitleID) as TextView
private var noteText = itemView.findViewById(R.id.listTextID) as TextView
var mList = list
init
{
itemView.setOnClickListener(this)
}
fun bindItems(note: UserNotes)
{
//var id = note.noteID
noteTitle.text = note.noteTitle
noteText.text = note.noteText
}
override fun onClick(v: View?)
{
var mPosition: Int = adapterPosition //For forwarding position to ref activity
var note = mList[mPosition]
adapPos = adapterPosition
var cardID = Intent(itemView.context, ReferenceActivity::class.java)
cardID.putExtra("cardPosition", mPosition)
cardID.putExtra("cardID", note.noteID) //TODO THIS IS FETCHING NOTE_ID VALUES FROM DB
Toast.makeText(itemView.context, "card id = " + note.noteID, Toast.LENGTH_SHORT).show()
itemView.context.startActivity(cardID)
}
}
fun updateResults(items: ArrayList<UserNotes>)
{
this.noteslist = items
}
Yes, I know its hard to go through someones code. I have tried to fix it many times sitting all day trying to figure it out and still no luck. I am requesting you to please help me with this. I don't care if you do in Java, I just need a way to fix it.

Declare your MainActivity in AndroidManifest.xml file like this.
android:launchMode="singleTask"
Before Finishing 'ReferenceActivity' (i.e calling finish()), make an Intent call to your 'MainActivity'. Also, override 'onNewIntent()' method inside your 'MainActivity'.
#Override
protected void onNewIntent(Intent intent)
Inside 'onNewIntent()' method, load you data again as you're already doing in 'onStart()' and 'onRestart()'.
Explanation, What we're doing is adding a single reference of our 'MainActivity' in Activity Stack, so whenever an intent call is made to 'MainActivity' instead of creating multiple reference it just create one reference and call newIntentMethod every time.
Hopefully, that will fix your issue.

Related

Why is the .update("field","value") not working?

If you see the read messages function in my activity class below, i wanted to update the isSeen field in firestore, but for some reason it does not work at all. My guess it requires a specific document value but that would not be possible as this a messaging app so there will be a lot of documents created.
Activity Class
class MessageActivity : AppCompatActivity() {
private lateinit var binding: ActivityMessageBinding
private lateinit var chat: ArrayList<Message>
private lateinit var messageAdapter: MessageAdapter
private lateinit var roomID: String
private lateinit var userID: String
private lateinit var recID: String
private var c: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMessageBinding.inflate(layoutInflater)
setContentView(binding.root)
userID = FirebaseAuth.getInstance().uid.toString()
recID = intent.getStringExtra("userID").toString()
val recName:String = intent.getStringExtra("userName").toString()
binding.userName.text = recName
chat = arrayListOf()
messageAdapter = MessageAdapter(chat)
binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
when {
userID < recID ->
{
roomID = userID + recID
}
userID.compareTo(recID) == 0 ->
{
Toast.makeText(this, "Error you are chatting with yourself!!!", Toast.LENGTH_SHORT).show()
}
else -> {
roomID = recID + userID
}
}
readMessages(userID,recID)
binding.btnSend.setOnClickListener {
val message: String = binding.textSend.text.toString()
if(message.isNotEmpty()){
sendMessage(userID,recID,message)
binding.textSend.text.clear()
}
else{
Toast.makeText(this,"You can't send empty message", Toast.LENGTH_SHORT).show()
}
}
binding.gps.setOnClickListener {
val uri = "http://maps.google.com/maps?daddr="
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(uri))
intent.setPackage("com.google.android.apps.maps")
startActivity(intent)
}
}
private fun sendMessage(sender: String, rec: String, message: String){
val db = Firebase.firestore
val time: FieldValue = FieldValue.serverTimestamp()
val msg = hashMapOf(
"userID" to sender,
"recID" to rec,
"message" to message,
"time" to time,
"roomID" to roomID,
"isSeen" to false
)
db.collection("chats").document(roomID).collection("messages").document().set(msg,SetOptions.merge())
}
private fun readMessages(userId: String, recId: String){
val rootRef = Firebase.firestore
rootRef.collection("chats").document(roomID).collection("messages").orderBy("time", Query.Direction.ASCENDING).addSnapshotListener(object : EventListener<QuerySnapshot?>
{
override fun onEvent(#Nullable documentSnapshots: QuerySnapshot?, #Nullable e: FirebaseFirestoreException?)
{
if (e != null)
{
Log.e(TAG, "onEvent: Listen failed.", e)
return
}
chat.clear()
if (documentSnapshots != null)
{
for (queryDocumentSnapshots in documentSnapshots)
{
val msg = queryDocumentSnapshots.toObject(Message::class.java)
if (msg.recID == recId && msg.userID == userId || msg.recID == userId && msg.userID == recId)
{
chat.add(msg)
}
if(msg.recID.equals(userID).and(msg.userID.equals(recID))){
rootRef.collection("chats").document(roomID).collection("messages").document().update("isSeen",true)
}
messageAdapter = MessageAdapter(chat)
binding.recyclerView.adapter = messageAdapter
}
}
}
})
}
}
Adapter Class
class MessageAdapter(private val MessageList:ArrayList<Message>):RecyclerView.Adapter<MessageAdapter.MessageViewHolder>() {
private val left = 0
private val right = 1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder {
return if(viewType==right){
val view1 = LayoutInflater.from(parent.context).inflate(R.layout.chat_sender_item,parent,false)
MessageViewHolder(view1)
}else{
val view2 = LayoutInflater.from(parent.context).inflate(R.layout.chat_receiver_item,parent,false)
MessageViewHolder(view2)
}
}
override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
val message:Message = MessageList[position]
holder.showMessage.text = message.message
if(position==MessageList.size-1){
if(message.isSeen)
{
holder.textSeen.text = "Seen"
}else{
holder.textSeen.text = "Delivered"
}
}else{
holder.textSeen.visibility = View.GONE
}
}
override fun getItemCount(): Int {
return MessageList.size
}
class MessageViewHolder(itemView:View) : RecyclerView.ViewHolder(itemView){
val showMessage: TextView = itemView.findViewById(R.id.showMessage)
val textSeen: TextView = itemView.findViewById(R.id.textSeen)
}
override fun getItemViewType(position: Int): Int {
val userID = FirebaseAuth.getInstance().currentUser!!.uid
return if(MessageList[position].userID==userID)
{
right
}else
{
left
}
}
}
Model Class
package com.aarondcosta99.foodreuseapp.model
data class Message(var userID:String? = "",var message:String? = "",var recID:String? = "",var isSeen:Boolean=false)
Firestore
This won't work:
rootRef.collection("chats").document(roomID).collection("messages").document().update("isSeen",true)
The document() call without any arguments creates a reference to a new non-existing document, which you then try to update. But update() only works when a document already exists, you can't use update() to create a document, so this code ends up doing nothing.
To update a document, you need to specify the complete path to that document. The fact that you need to update a lot of documents makes no difference to that fact, it just means you'll need to paths to a lot of documents.
As far as I can tell, you are trying to update the document that you read in documentSnapshots, which means you already have the DocumentReference handy and can update it with:
queryDocumentSnapshots.reference.update("isSeen",true)

Android Kotlin: How can I delete the data from Firebase

I am a new about Android Kotlin. I try to delete the data from Cloud Firebase when I click the button on my app. I did some necessary code on my adapter but How can ı call the database collection on my Activity? ı shared the my adapter and my Activity code below.
class NoteAdapter(val titleText: ArrayList<String>, val rowImage: ArrayList<String>, val noteText: ArrayList<String>, val listener: onClick) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
interface onClick {
fun onItemClickListener(v: View, pos: Int, data: Any)
}
class NoteHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemImage : ImageView = itemView.findViewById(R.id.recyclerImage)
val itemDelete: ImageView = itemView.findViewById(R.id.delete)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recycler_row, parent, false)
return NoteHolder(v)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.itemView.recyclerTitleText.text = titleText[position]
Picasso.get().load(rowImage[position]).resize(150,150).into(holder.itemImage)
holder.itemView.setOnClickListener {
val intent = Intent(holder.itemView.context, PastNotesActivity:: class.java)
intent.putExtra("oldTitle", titleText[position])
intent.putExtra("oldNote", noteText[position])
intent.putExtra("oldImage", rowImage[position])
holder.itemView.context.startActivity(intent)
}
holder.itemDelete.setOnClickListener { v: View ->
titleText.removeAt(position)
noteText.removeAt(position)
rowImage.removeAt(position)
notifyItemRemoved(position)
listener.onItemClickListener(v, position, holder.itemView)
}
}
override fun getItemCount(): Int {
return titleText.size
}
override fun getItemId(position: Int):Long {
return position.toLong()
}
override fun getItemViewType(position: Int):Int {
return position
}
}
And This is my Activity code, I create the itemDelete fun in this Activity but How can I define my adapter code in this fun? and I tried to write "{id.data}" in my document but did not work what should I write ?
class ListViewActivity : AppCompatActivity() {
var selectedPicture: Uri? = null
private lateinit var auth: FirebaseAuth
private lateinit var db : FirebaseFirestore
var titleTextFromFB : ArrayList<String> = ArrayList()
var noteTextFromFB : ArrayList<String> = ArrayList()
var imageFromFB : ArrayList<String> = ArrayList()
var adapter: NoteAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_view)
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
// recyclerview
var layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
// adapter = NoteAdapter(titleTextFromFB, imageFromFB, noteTextFromFB)
//recyclerView.adapter = adapter
adapter = NoteAdapter(titleTextFromFB, imageFromFB, noteTextFromFB, object: NoteAdapter.onClick{
override fun onItemClickListener(v: View, pos: Int, data: Any) {
when(v.id){
R.id.delete -> itemDelete(data)
}
}
})
recyclerView.adapter = adapter
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val menuInflater = menuInflater
menuInflater.inflate(R.menu.add_note, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.add_note_click) {
// Take Notes Activity
val intent = Intent(applicationContext, TakeNotesActivity::class.java)
intent.putExtra("info","new")
startActivity(intent)
} else if (item.itemId == R.id.log_out) {
val alert = AlertDialog.Builder(this)
alert.setTitle("Log Out")
alert.setMessage("Are you sure to logout from the app ?")
alert.setPositiveButton("Yes") {dialog, which ->
auth.signOut()
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
alert.setNegativeButton("No") {dialog, which ->
}
alert.show()
}
return super.onOptionsItemSelected(item)
}
// get data from firestore
fun getDataFromFirestore() {
db.collection("Notes").orderBy("date", Query.Direction.DESCENDING).addSnapshotListener{ snapshot, exception ->
if (exception != null) {
// If there is a error ,
Toast.makeText(applicationContext, exception.localizedMessage.toString(), Toast.LENGTH_LONG).show()
} else {
if (snapshot != null) {
if (!snapshot.isEmpty) {
titleTextFromFB.clear()
noteTextFromFB.clear()
imageFromFB.clear()
val documents = snapshot.documents
for (document in documents) {
val userEmail = document.get("userEmail") as String
val noteTitle = document.get("noteTitle") as String
val yourNote = document.get("yourNote") as String
val downloadUrl = document.get("downloadUrl") as String
val timestamp = document.get("date") as Timestamp
val date = timestamp.toDate()
titleTextFromFB.add(noteTitle)
imageFromFB.add(downloadUrl)
noteTextFromFB.add(yourNote)
adapter!!.notifyDataSetChanged()
}
}
}
}
}
}
fun itemDelete(data: Any) {
db.collection("Notes").document().delete().addOnSuccessListener {
}
.addOnFailureListener { exception ->
Toast.makeText(applicationContext, exception.localizedMessage.toString(), Toast.LENGTH_LONG).show()
}
}
}
This code won't work:
db.collection("Notes").document().delete().addOnSuccessListener {
The db.collection("Notes").document() call creates a reference to a new document, which you then delete. So nothing happens.
What you need to do is determine the ID of the document that the user clicked on, and pass that into the document(...) call. That gives you a DocumentReference to the correct document, so that the delete() call will then delete that document.
The key to determining the ID of the document the user clicked on is in this code:
adapter = NoteAdapter(titleTextFromFB, imageFromFB, noteTextFromFB, object: NoteAdapter.onClick{
override fun onItemClickListener(v: View, pos: Int, data: Any) {
when(v.id){
R.id.delete -> itemDelete(data)
}
}
})
You will need to determine the ID from one of these parameters: v, post, or data. I typically do this by keeping a list of document IDs or DocumentSnapshot objects in my adapter or activity, and then looking the clicked item up by its position/index.
So in your getDataFromFirestore function, add the snapshots to a list that you've defined as a field in your activity class:
// in your activity, declare a list;
var mDocuments: List<DocumentSnapshot>? = null
// Then in getDataFromFirestore store that list
...
mDocuments = snapshot.documents;
val documents = snapshot.documents
for (document in documents) {
...
}
...
// And use it when calling itemDelete:
adapter = NoteAdapter(titleTextFromFB, imageFromFB, noteTextFromFB, object: NoteAdapter.onClick{
override fun onItemClickListener(v: View, pos: Int, data: Any) {
when(v.id){
R.id.delete -> itemDelete(mDocuments[pos])
}
}
})
// To then finally delete the document by its ID
fun itemDelete(doc: DocumentSnapshot) {
db.collection("Notes").document(doc.Id).delete()
}

Android Kotlin: Wrong images showing in RecyclerView. How can I fix it?

the pictures I show in my recyclerview list are mixed with each other. Let's say that letter A indicates photo X, and letter B indicates photo Y. On my Recyclerview list, A is showing Y photo, and B shows the photo X.When I open the application again, there is no problem. I've been dealing with this problem for a few days, I've tried many solutions but I did not find a true solution until now. How should I solve it? Thanx
If you want to look my adapter and activity code, ı shared the below
My adapter:
class NoteAdapter(private var titleText: ArrayList<String>, private var imageButton: ArrayList<String>, private var noteText: ArrayList<String>) : RecyclerView.Adapter<NoteAdapter.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val itemTitle : TextView = itemView.findViewById(R.id.recyclerTitleText)
val itemImage : ImageView = itemView.findViewById(R.id.recyclerImage)
init {
itemView.setOnClickListener { v: View ->
// Toast.makeText(itemView.context,"You clicked on item # ${position + 1}", Toast.LENGTH_SHORT).show()
val intent = Intent(itemView.context, PastNotesActivity::class.java)
intent.putExtra("oldTitle", titleText[position])
intent.putExtra("oldNote", noteText[position])
intent.putExtra("oldImage", imageButton[position])
itemView.context.startActivity(intent)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recycler_row, parent, false)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemTitle.text = titleText[position]
Picasso.get().load(imageButton[position]).resize(150,150).into(holder.itemImage)
}
override fun getItemCount(): Int {
return titleText.size
}
}
My Activity
class ListViewActivity : AppCompatActivity() {
private lateinit var auth: FirebaseAuth
private lateinit var db : FirebaseFirestore
var titleTextFromFB : ArrayList<String> = ArrayList()
var noteTextFromFB : ArrayList<String> = ArrayList()
var imageFromFB : ArrayList<String> = ArrayList()
var adapter: NoteAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_view)
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
getDataFromFirestore()
// recyclerview
var layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
adapter = NoteAdapter(titleTextFromFB, imageFromFB, noteTextFromFB)
recyclerView.adapter = adapter
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val menuInflater = menuInflater
menuInflater.inflate(R.menu.add_note, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.add_note_click) {
// Take Notes Activity
val intent = Intent(applicationContext, TakeNotesActivity::class.java)
intent.putExtra("info","new")
startActivity(intent)
} else if (item.itemId == R.id.log_out) {
val alert = AlertDialog.Builder(this)
alert.setTitle("Log Out")
alert.setMessage("Are you sure to logout from the app ?")
alert.setPositiveButton("Yes") {dialog, which ->
auth.signOut()
val intent = Intent(applicationContext, MainActivity::class.java)
startActivity(intent)
finish()
}
alert.setNegativeButton("No") {dialog, which ->
}
alert.show()
}
return super.onOptionsItemSelected(item)
}
// get data from firestore
fun getDataFromFirestore() {
db.collection("Notes").orderBy("date", Query.Direction.DESCENDING).addSnapshotListener{ snapshot, exception ->
if (exception != null) {
// If there is a error ,
Toast.makeText(applicationContext, exception.localizedMessage.toString(), Toast.LENGTH_LONG).show()
} else {
if (snapshot != null) {
if (!snapshot.isEmpty) {
titleTextFromFB.clear()
noteTextFromFB.clear()
val documents = snapshot.documents
for (document in documents) {
val userEmail = document.get("userEmail") as String
val noteTitle = document.get("noteTitle") as String
val yourNote = document.get("yourNote") as String
val downloadUrl = document.get("downloadUrl") as String
val timestamp = document.get("date") as Timestamp
val date = timestamp.toDate()
titleTextFromFB.add(noteTitle)
imageFromFB.add(downloadUrl)
noteTextFromFB.add(yourNote)
adapter!!.notifyDataSetChanged()
}
}
}
}
}
}
}

how to filter list data in descending order in kotlin?

From the Android app, I wrote for I want to add, add a button in the toolbar that acts as a toggle. When the toggle is disabled (the default state) all posts should be shown, when it is enabled (after a tap) the list should only show the posts having user_id set to 1 and sorted by descending published_at. Tapping on the button again will return it to its default state.
Note that publishedAt returning date and publishedAt and user_id coming from postList from the server I want to know how can I implement above requirement what kind of steps should I have to follow
below my logic implementation in MainActivity.kt
class MainActivity : AppCompatActivity() {
#Inject
lateinit var restInterface: RestInterface
private fun initializeDagger() = App.appComponent.inject(this)
var context: Context? = null
private var filteredList: List<Post>? = null
private var recyclerView: RecyclerView? = null
private var switch1: Switch? = null
private var restAdapter: RestAdapter? = null
private var postList: List<Post>? = null
private var restList: RestList? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeDagger()
recyclerView = findViewById(R.id.recycler_view)
switch1 = findViewById(R.id.switch1)
fetchPosts()
switch1.setOnclickListener {
postList.forEach { postItem: Post ->
if (postItem.userId == 1)
filteredList.add(postItem)
}
recyclerView.post = filteredList
recyclerView.notifyDatasetChanged()
}
// Collections.sort( filteredList.get(4).publishedAt, Collections.reverseOrder());
}
private fun fetchPosts() {
val progress = ProgressDialog(this)
progress.setMessage("Loading... ")
progress.isIndeterminate = true
progress.show()
restInterface?.getPosts?.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableSingleObserver<Response<RestList>>() {
override fun onSuccess(response: Response<RestList>) {
restList = response.body()
val layoutManager = LinearLayoutManager(applicationContext)
recyclerView?.layoutManager = layoutManager
// initialize postList with posts
postList = restList?.posts
restAdapter = postList?.let { RestAdapter(it, restList) }
recyclerView?.adapter = restAdapter
}
override fun onError(e: Throwable) {
progress.dismiss()
Toast.makeText(context, "" + e.message, Toast.LENGTH_SHORT).show()
}
})
}
}
below my RestAdapter.kt
class RestAdapter(val post: List<Post>,val restList: RestList?) : RecyclerView.Adapter<RestAdapter.PostHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.post_list, null)
return PostHolder(itemView)
}
override fun getItemCount(): Int {
return post.size
}
override fun onBindViewHolder(holder: PostHolder, position: Int) {
val posts = post[position]
Picasso
.get() // give it the context
.load(posts.image) // load the image
.into(holder.postImage)
holder.userId.text = posts.userId.toString()
holder.postTitle.text = posts.title
holder.postTime.text = posts.publishedAt
holder.postDescription.text = posts.description
}
class PostHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val postImage: ImageView = itemView.findViewById(R.id.postImage)
val userId: TextView = itemView.findViewById(R.id.userId)
val postTitle: TextView = itemView.findViewById(R.id.postTitle)
val postTime: TextView = itemView.findViewById(R.id.postTime)
val postDescription: TextView = itemView.findViewById(R.id.postDescription)
}
}
You can use method .sortedByDescending or .sortedBy on list :)
[Edit]
I give you a simple solution for that, maybe not the best but should work
1. Change RestAdapter constructor to (var post: List,val restList: RestList?)
Add method to updateData in apadapter:
fun filterData(isChecked: Boolean) {
if (isChecked) {
val filteredList = arrayListOf<Post>()
filteredList.addAll(restList?posts.filter { it.user_id == 1 }.sortedByDescending { it.published_at })
post = filteredList
} else {
post = restList?.posts
}
notifyDatasetChanged()
}
And in your class use this:
switch1.setOnclickListener {
restAdapter?.filterData(switch1.isChecked()//state of your switch i.e isChecked() which return true or false)
}

RecyclerView list item does not show when entries are made to the Room database

I am trying to rewrite an existing app in Kotlin for the purpose of learning and getting used to the language. The app allows the user to enter and modify entries and each entry is hosted in a RecyclerView list with a custom list item. Although entries are successfully added to the database (confirming that with Toast messages), there isn't a list item present for the said entry.
This is my adapter:
class EntryAdapter : androidx.recyclerview.widget.ListAdapter<Entry, EntryAdapter.ViewHolder>(DIFF_CALLBACK){
private var listener: OnItemLongClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val currentEntry = getItem(position)
holder.hint.text = currentEntry.hint
holder.username.text = currentEntry.username
holder.password.text = currentEntry.password
}
fun getEntryAt(position: Int): Entry{return getItem(position)}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val username: TextView = itemView.findViewById(R.id.username_display)
val password: TextView = itemView.findViewById(R.id.password_display)
val hint: TextView = itemView.findViewById(R.id.hint_display)
init {
itemView.setOnLongClickListener{
val position = adapterPosition
if (listener != null && position != RecyclerView.NO_POSITION){listener!!.onItemLongClick(getItem(position))}
true
}
}
}
interface OnItemLongClickListener{fun onItemLongClick(entry: Entry)}
fun setOnItemLongClickListener(listener: OnItemLongClickListener){this.listener = listener}
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Entry>(){
override fun areItemsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.id == newItem.id}
override fun areContentsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.username == newItem.username && oldItem.hint == newItem.hint && oldItem.password == newItem.password}
}
}
}
This is my AddEditEntry.kt activity. The way it works is that when the user wishes to make an entry, he/she clicks on the FAB button which invokes this activity. This activity is where the user enters the entry and adds it to the database (and by extension, the RecyclerView) by clicking the saveEntry button:
class AddEditEntryActivity : AppCompatActivity() {
private var usernameEditText: EditText? = null
private var passwordEditText: EditText? = null
private var hintEditText: EditText? = null
private var passwordABCD: CheckBox? = null
private var passwordabcd: CheckBox? = null
private var password0123: CheckBox? = null
private var passwordSymbols: CheckBox? = null
private var radio4: RadioButton? = null
private var radio8: RadioButton? = null
private var radio12: RadioButton? = null
private var radio16: RadioButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_addedit_entry)
usernameEditText = findViewById(R.id.username_field)
passwordEditText = findViewById(R.id.password_field)
hintEditText = findViewById(R.id.hint_field)
passwordABCD = findViewById(R.id.upp_checkbox)
passwordabcd = findViewById(R.id.low_checkbox)
password0123 = findViewById(R.id.num_checkbox)
passwordSymbols = findViewById(R.id.sym_checkbox)
radio4 = findViewById(R.id.four)
radio8 = findViewById(R.id.eight)
radio12 = findViewById(R.id.twelve)
radio16 = findViewById(R.id.sixteen)
val generatePassword = findViewById<Button>(R.id.btn_password_generate)
val saveEntry = findViewById<Button>(R.id.btn_save)
val intent = intent
if (intent.hasExtra(EXTRA_ID)) {
title = getString(R.string.edit_entry)
saveEntry.setText(R.string.update_entry)
usernameEditText!!.setText(getIntent().getStringExtra(EXTRA_USERNAME))
passwordEditText!!.setText(getIntent().getStringExtra(EXTRA_PASSWORD))
hintEditText!!.setText(getIntent().getStringExtra(EXTRA_HINT))
}
else {title = "Add Entry"}
Objects.requireNonNull<ActionBar>(supportActionBar).setHomeAsUpIndicator(R.drawable.ic_close_white_24dp)
generatePassword.setOnClickListener { passwordEditText!!.setText(generatedPassword()) }
saveEntry.setOnClickListener {
val data = Intent()
data.putExtra(EXTRA_USERNAME, usernameEditText!!.text.toString())
data.putExtra(EXTRA_HINT, hintEditText!!.text.toString())
data.putExtra(EXTRA_PASSWORD, passwordEditText!!.text.toString())
val id = getIntent().getIntExtra(EXTRA_ID, -1)
if (id != -1) {data.putExtra(EXTRA_ID, id)}
setResult(Activity.RESULT_OK, data)
finish()
Toast.makeText(this, "data.putExtra() from AddEditEntryActivity", Toast.LENGTH_SHORT).show()
Toast.makeText(this, usernameEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, hintEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, passwordEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
}
}
private fun generatedPassword(): String? {
var length = 0
val generatedString = StringBuilder()
val rand = Random()
val capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
val lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"
val numbers = "0123456789"
val characters = "!##$%^&*()"
if (radio4!!.isChecked) {length = 4}
else if (radio8!!.isChecked) {length = 8}
else if (radio12!!.isChecked) {length = 12}
else if (radio16!!.isChecked) {length = 16}
var totalCharacters = ""
if (passwordABCD!!.isChecked) {totalCharacters += capitalLetters}
if (passwordabcd!!.isChecked) {totalCharacters += lowercaseLetters}
if (password0123!!.isChecked) {totalCharacters += numbers}
if (passwordSymbols!!.isChecked) {totalCharacters += characters}
if (!totalCharacters.trim { it <= ' ' }.isEmpty() && length > 0) {
for (i in 0 until length) {generatedString.append(totalCharacters[rand.nextInt(totalCharacters.length)])}
return generatedString.toString()
}
else {Toast.makeText(this, "Not a valid password!", Toast.LENGTH_SHORT).show()}
return null
}
companion object {
val EXTRA_USERNAME = "com.ozbek.cryptpass.EXTRA_USERNAME"
val EXTRA_HINT = "com.ozbek.cryptpass.EXTRA_HINT"
val EXTRA_PASSWORD = "com.ozbek.cryptpass.EXTRA_PASSWORD"
val EXTRA_ID = "com.ozbek.cryptpass.EXTRA_ID"
}
}
And this is the MainActivity.kt file where the user enters new entries. The first if block in onActivityResult() is the code that retrieves the entries from AddEditEntry.kt file and adds it to the entity class:
class MainActivity : AppCompatActivity(), LifecycleOwner {
private lateinit var recyclerView: RecyclerView
internal lateinit var adapter: EntryAdapter
private lateinit var floatingActionButton: FloatingActionButton
private lateinit var layoutManager: RecyclerView.LayoutManager
private lateinit var addEditEntryActivity: AddEditEntryActivity
internal lateinit var entry: Entry
private lateinit var viewModel: EntryViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ctx = this.applicationContext
val sentryDsn = "https://93f06d5090d646ac9443a3fc531fc5de#sentry.io/1454826:port/1?options"
Sentry.init(sentryDsn, AndroidSentryClientFactory(ctx))
Sentry.init(AndroidSentryClientFactory(ctx))
viewModel = ViewModelProviders.of(this).get(EntryViewModel::class.java)
viewModel.allEntries.observe(this, Observer { entries -> adapter.submitList(entries) })
adapter = EntryAdapter()
layoutManager = LinearLayoutManager(this)
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
addEditEntryActivity = AddEditEntryActivity()
entry = Entry()
floatingActionButton = findViewById<FloatingActionButton>(R.id.generate_fab)
floatingActionButton.setOnClickListener {
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
Toast.makeText(this, "AddEditActivity started", Toast.LENGTH_SHORT).show()
startActivityForResult(intent, ADD_ENTRY_REQUEST)
}
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {return false}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {viewModel.delete(adapter.getEntryAt(viewHolder.adapterPosition))}
}).attachToRecyclerView(recyclerView)
adapter.setOnItemLongClickListener (object: EntryAdapter.OnItemLongClickListener {
override fun onItemLongClick(entry: Entry){
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
intent.putExtra(AddEditEntryActivity.EXTRA_ID, entry.id)
intent.putExtra(AddEditEntryActivity.EXTRA_USERNAME, entry.username)
intent.putExtra(AddEditEntryActivity.EXTRA_HINT, entry.hint)
intent.putExtra(AddEditEntryActivity.EXTRA_PASSWORD, entry.password)
startActivityForResult(intent, EDIT_ENTRY_REQUEST)
}
})
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val itemId = item.itemId
if (itemId == R.id.delete_all) {viewModel.deleteAll()}
return super.onOptionsItemSelected(item)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ADD_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password)
viewModel.insert(entry)
Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show()
} else if (requestCode == EDIT_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data!!.getIntExtra(AddEditEntryActivity.EXTRA_ID, -1), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val id = Objects.requireNonNull<Intent>(data).getIntExtra(AddEditEntryActivity.EXTRA_ID, -1)
if (id == -1) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
return
}
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password, id)
entry.id = id
viewModel.update(entry)
Toast.makeText(this, "Entry updated", Toast.LENGTH_SHORT).show()
} else {Toast.makeText(this, "Entry not added!", Toast.LENGTH_SHORT).show()}
}
companion object {
const val ADD_ENTRY_REQUEST = 1
const val EDIT_ENTRY_REQUEST = 2
}
}
I can add more code per request. This is the full Github repo.
Here is the problem:
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
Idk what's going on but it seems like you're initializing them to themselves. Don't do that. Its good practice to keep your variable names distinct. Refactor the objects like this:
internal lateinit var entryAdapter: EntryAdapter
private lateinit var linearLayoutManager: RecyclerView.LayoutManager
And make the following changes in your onCreate():
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
adapter = entryAdapter
layoutManager = linearLayoutManager
}

Categories

Resources