I'm trying to integrate chatkit into my Android app grabbing portions of code from this getting started tutorial and this android-public-demo-app project on github and I am getting this error:
D/ChatRoomsActivity: on subscripetoRoomMultipart reason:: Room membership required.
The user is already a member of the room which is producing is an error according to the dashboard/console snippets which are shown at the bottom of this post. Currently the currentUser is: user id=username2-PCKid
Error occurs in ChatRoomAcitivity.kt at currentUser.subscribeToRoomMultipart. I included the ChatRoomListActivity and adapters for context.
Any and all help is appreciated. Please let me know if more context is required.
here is my ChatRoomListActivity.kt
class ChatRoomsListActivity : AppCompatActivity() {
val adapter = ChatRoomsListAdapter();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room_list)
initRecyclerView()
initChatManager()
}
private fun initRecyclerView() {
recycler_view.layoutManager = LinearLayoutManager(this#ChatRoomsListActivity)
recycler_view.adapter = adapter
}
private fun initChatManager() {
val chatManager = ChatManager(
instanceLocator = "************",
userId = "username2-PCKid",
dependencies = AndroidChatkitDependencies(
tokenProvider = ChatkitTokenProvider(
endpoint = "******************",
userId = "username2-PCKid"
)
)
)
chatManager.connect(listeners = ChatListeners(
)
, callback = { result ->
when (result) {
is Result.Success -> {
// We have connected!
Log.d(AppActivityTags.chatRoomsListActivityTAG, "chatManager connected!")
val currentUser = result.value
AppController.currentUser = currentUser
Log.d(AppActivityTags.chatRoomsListActivityTAG, "user: " + currentUser + " is logged in to chatkit")
val userJoinedRooms = ArrayList<Room>()
for (x in currentUser.rooms) {
adapter.addRoom(x)
recycler_view.smoothScrollToPosition(0)
}
adapter.notifyDataSetChanged()
Log.d(AppActivityTags.chatRoomsListActivityTAG, "joined rooms.size: " + userJoinedRooms.size.toString());
adapter.setInterface(object : ChatRoomsListAdapter.RoomClickedInterface {
override fun roomSelected(room: Room) {
Log.d(AppActivityTags.chatRoomsListActivityTAG, "Room clicked!")
if (room.memberUserIds.contains("username2-PCKid")) {
// if (room.memberUserIds.contains(currentUser.id)) { <-- OG code
// user already belongs to this room
roomJoined(room)
Log.d("roomSelected", "user already belongs to this room: " + roomJoined(room))
} else {
currentUser.joinRoom(
roomId = room.id,
callback = { result ->
when (result) {
is Result.Success -> {
// Joined the room!
roomJoined(result.value)
}
is Result.Failure -> {
Log.d(AppActivityTags.chatRoomsListActivityTAG, result.error.toString())
}
}
}
)
}
}
})
}
is Result.Failure -> {
// Failure
Log.d(AppActivityTags.chatRoomsListActivityTAG, "ChatManager connection failed"
+ result.error.toString())
}
}
})
}
private fun roomJoined(room: Room) {
val intent = Intent(this#ChatRoomsListActivity, ChatRoomActivity::class.java)
Log.d(AppActivityTags.chatRoomsListActivityTAG, "function roomJoined activated")
intent.putExtra("room_id", room.id)
intent.putExtra("room_name", room.name)
startActivity(intent)
}
}
here is my ChatRoomListAdapter.kt
class ChatRoomsListAdapter: RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
private var list = ArrayList<Room>()
private var roomClickedInterface: RoomClickedInterface? = null
fun addRoom(room:Room){
list.add(room);
Log.d(AppActivityTags.chatRoomsListAdapterTAG, "Room name: " + room.name)
Log.d(AppActivityTags.chatRoomsListAdapterTAG, "Room id: " + room.id)
Log.d(AppActivityTags.chatRoomsListAdapterTAG, "Room memberUserIds: " + room.memberUserIds)
Log.d(AppActivityTags.chatRoomsListAdapterTAG, "Room isPrivate: " + room.isPrivate)
}
fun setInterface(roomClickedInterface:RoomClickedInterface){
this.roomClickedInterface = roomClickedInterface
}
override fun getItemCount(): Int {
return list.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(
android.R.layout.simple_list_item_1,
parent,
false
)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.roomName.text = list[position].name
val context = holder.itemView.context
holder.itemView.setOnClickListener {
room = list[position]
val intent = Intent(context, ChatRoomActivity::class.java)
Log.d(AppActivityTags.chatRoomsListActivityTAG, "function roomJoined activated")
intent.putExtra("room_id", room.id)
intent.putExtra("room_name", room.name)
context.startActivity(intent)
}
}
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
Toast.makeText(itemView.context, "item was clicked", Toast.LENGTH_LONG).show()
val mContext = itemView.context
Log.d(AppActivityTags.chatRoomsListAdapterTAG, "Size of adapter: " + list.size.toString())
Log.d(AppActivityTags.chatRoomsListAdapterTAG, roomName.toString() + " roomName clicked")
}
var roomName: TextView = itemView.findViewById(android.R.id.text1)
init {
itemView.setOnClickListener(this)
}
}
interface RoomClickedInterface{
fun roomSelected(room:Room)
}
}
here is my ChatRoomActivity.kt
class ChatRoomActivity : AppCompatActivity() {
lateinit var adapter:ChatRoomAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room)
supportActionBar!!.title = intent.getStringExtra("room_name")
adapter = ChatRoomAdapter()
setUpRecyclerView()
val currentUser = AppController.currentUser
val roomId = intent.getStringExtra("room_id")
currentUser.subscribeToRoomMultipart(
roomId = roomId,
listeners = RoomListeners(
onMultipartMessage = { message ->
Log.d("TAG",message.toString())
// com.pusher.chatkit.messages.multipart.Message
runOnUiThread(Runnable{
adapter.addMessage(message)
recycler_view.layoutManager?.scrollToPosition(adapter.itemCount -1)
})
},
onErrorOccurred = { error ->
Log.d(AppActivityTags.chatRoomActivityTAG, "error.reason: " + error.reason)
Log.d(AppActivityTags.chatRoomActivityTAG, "currentuser.rooms: " + currentUser.rooms)
}
),
messageLimit = 100, // Optional
callback = { subscription ->
// Called when the subscription has started.
// You should terminate the subscription with subscription.unsubscribe()
// when it is no longer needed
}
)
button_send.setOnClickListener {
if (edit_text.text.isNotEmpty()){
currentUser.sendSimpleMessage(
roomId = roomId,
messageText = edit_text.text.toString(),
callback = { result -> //Result<Int, Error>
when (result) {
is Result.Success -> {
runOnUiThread {
edit_text.text.clear()
hideKeyboard()
}
}
is Result.Failure -> {
Log.d(AppActivityTags.chatRoomActivityTAG, "error # button_send.setOnclick: " + result.error.toString())
}
}
}
)
}
}
}
private fun hideKeyboard() {
val imm = this.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
var view = this.currentFocus
if (view == null) {
view = View(this)
}
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
private fun setUpRecyclerView() {
recycler_view.layoutManager= LinearLayoutManager(this#ChatRoomActivity)
recycler_view.adapter = adapter
}
}
here is my ChatRoomAdapter.kt
class ChatRoomAdapter: RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
private var list = ArrayList<Message>()
fun addMessage(message: Message){
list.add(message)
notifyDataSetChanged()
}
override fun getItemCount(): Int {
return list.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.custom_chat_row,parent,false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val inlineMessage: Payload.Inline = list[position].parts[0].payload as Payload.Inline
holder.userName.text = list[position].sender.name
holder.message.text = inlineMessage.content
}
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
var userName: TextView = itemView.findViewById(R.id.text_user_name)
var message: TextView = itemView.findViewById(R.id.chat_message)
}
}
I think I know what happened.
I think what happened was I created a room from the pusher-chat kit dashboard and then tried to sign in as them. and then enter the room as them. I was able to see my chatroomlists that they were affiliated with however I think that since I created the chatroom from the dashboard, it thought I was someone else.
Long story short, it works if I create the room from my android emulator and then go to the room. if I create the room from the dashboard and try to join, it doesn't seem to work.
Related
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)
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()
}
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()
}
}
}
}
}
}
}
I was implementing paging 3 in my app.
I tried to follow google codelabs and overview on paging 3 from documentation
However it fails to display the data in the adapter, I put logs in the adapter and no logs from adapter show up on logcat.
I tried to debug the codelabs code and data flow was exactly as mine, since I can't( or don't know yet) how to intervene paging which is received in flow in fragment, I am unable to tell if my fragment receives the data. Also I have never used adapter.submitdata() and new to coroutines, I am not sure if it could be the scope which is the problem.
Any help is appreciated. Thanks in advance.
Have a nice day.
fragment
transactionsAdapterPaging = TransactionsPagedListAdapter()
homeFragmentDataBinding.transactionsRV.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
adapter = transactionsAdapterPaging
}
homeFragmentDataBinding.transactionsRV.adapter = transactionsAdapterPaging
lifecycleScope.launch {
mainViewModel.getDataFromRemote().collectLatest {
Log.e(TAG, "initializeTransactionAdapter: ++++++++====== " + it.toString())
transactionsAdapterPaging.submitData(it)
}
}
viewmodel
private var currentSearchResult: Flow<PagingData<TransactionDetail>>? = null
fun getDataFromRemote(): Flow<PagingData<TransactionDetail>> {
val lastResult = currentSearchResult
val newResult: Flow<PagingData<TransactionDetail>> = mainRepository.getDataFromRemote()
.cachedIn(viewModelScope)
currentSearchResult = newResult
return newResult
}
repository
fun getDataFromRemote(): Flow<PagingData<TransactionDetail>> {
return Pager(
config = PagingConfig(pageSize = 1, enablePlaceholders = false),
pagingSourceFactory = { TransactionsDataSource() }
).flow
}
data source
class TransactionsDataSource() : PagingSource<Int, TransactionDetail>() {
private val TAG = "UserDataSource"
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TransactionDetail> {
val position = params.key ?: 1
return try {
lateinit var transactionDetail: List<TransactionDetail>
var response: Response<Any?>? = NewAPIServiceClient.getAllTransactionsPaging(
Application.getInstance(),
Prefs.getString(Constants.CARD_UUID_IN_USE, ""),
position,
null
)
Log.e(TAG, "load: " + response)
if (response != null && response.isSuccessful) {
if (response?.body() != null) {
val gson = Gson()
try {
val jsonObject1 = JSONObject(gson.toJson(response?.body()))
val transactionsGson = jsonObject1.getJSONArray(Constants.PN_DATA_NEW)
transactionDetail = gson.fromJson(
transactionsGson.toString(),
object : TypeToken<List<TransactionDetail>?>() {}.type
)
} catch (e:Exception){
}
}
}
val repos = transactionDetail
Log.e(TAG, "load: ===== " + repos.toString() )
return LoadResult.Page(
data = repos,
prevKey = if (position == 1) null else position - 1,
nextKey = if (repos.isEmpty()) null else position + 1
)
} catch (exception: IOException) {
Log.e(TAG, "load: 11111111111111111 " + exception )
LoadResult.Error(exception)
} catch (exception: HttpException) {
Log.e(TAG, "load: 11111111111111111 " + exception )
LoadResult.Error(exception)
}
}
}
adapter
class TransactionsPagedListAdapter() :
PagingDataAdapter<TransactionDetail, RepoViewHolder>(
DIFF_CALLBACK
) {
private val TAG = "AdapterPaging"
private var serviceListFiltered: List<TransactionDetail>? = null
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RepoViewHolder {
Log.e(TAG, "onCreateViewHolder: ")
val itemBinding: RvListItemTransactionsHomeBinding =
RvListItemTransactionsHomeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return RepoViewHolder(itemBinding)
}
override fun onBindViewHolder(
holder: RepoViewHolder,
position: Int
) {
Log.e(TAG, "onBindViewHolder: ")
val repoItem = getItem(position)
if (repoItem != null) {
(holder).bind(repoItem, position)
}
}
override fun getItemCount(): Int {
return if (serviceListFiltered == null) {
0
} else serviceListFiltered!!.size
}
companion object {
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<TransactionDetail>() {
override fun areItemsTheSame(oldItem: TransactionDetail, newItem: TransactionDetail) =
oldItem.uuid == newItem.uuid
override fun areContentsTheSame(
oldItem: TransactionDetail,
newItem: TransactionDetail
) =
oldItem == newItem
}
}
}
class RepoViewHolder internal constructor(itemBinding: RvListItemTransactionsHomeBinding) :
RecyclerView.ViewHolder(itemBinding.getRoot()), View.OnClickListener {
private val mDataBinding: RvListItemTransactionsHomeBinding = itemBinding
var rootView: View
fun bind(invoice: TransactionDetail, position: Int) {
rootView.transaction_text_title.text = invoice.merchant?.merchantName
var amountWithSymbol =
Utilities.getFormattedAmount(
Application.getInstance()?.applicationContext,
invoice.amount.toString()
)
rootView.transaction_amount.text = amountWithSymbol
rootView.transactions_date.text = invoice.timestamp
}
override fun onClick(view: View) {
if (adapterPosition > RecyclerView.NO_POSITION) {
}
}
init {
val itemView: View = itemBinding.getRoot()
rootView = mDataBinding.constraintContainer
itemView.setOnClickListener(this)
}
}
`override fun getItemCount(): Int {
return if (serviceListFiltered == null) {
0
} else serviceListFiltered!!.size
}`
Don't override getItemCount in your recyclerview adapter.
I'm trying to view all already joined rooms in pusher chatkit recycler view however it doesn't seem to be working. It only loads one room after I scroll. When I open the activity, it shows blank. Also, when I click on the room, the intent isn't started, it just stays on the recycler view. It should be going to the selected chat room.
Through Debug, I'm able to see that I am connecting to the accessible rooms(there's 7 of them) however it only seems like one of them is being added to the adapter/recyclerview however I can't find where it is falling short. I am relatively new to kotlin which probably adds to the issue.
Here is the tutorial I am following which I thought would be relatively good to go out of the box but it seems there are some snags.
any and all help is appreciated.
ChatRoomsListActivity
class ChatRoomsListActivity : AppCompatActivity() {
val adapter = ChatRoomsListAdapter();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_room_list)
initRecyclerView()
initChatManager()
}
private fun initRecyclerView() {
recycler_view.layoutManager = LinearLayoutManager(this#ChatRoomsListActivity)
recycler_view.adapter = adapter
/*
recycler_view.apply {
val topSpacingDecorator = TopSpacingItemDecoration(30)
addItemDecoration(topSpacingDecorator)
recycler_view.layoutManager = LinearLayoutManager(this#ChatRoomsListActivity)
recycler_view.adapter = adapter
adapter = ChatRoomsListAdapter();
adapter = adapter
}
*/
}
private fun initChatManager() {
val chatManager = ChatManager(
instanceLocator = "xxxxxxxxxxxxxx",
userId = "username1-PCKid",
dependencies = AndroidChatkitDependencies(
tokenProvider = ChatkitTokenProvider(
endpoint = "yyyyyyyyyyyyyyyyyyy",
// endpoint = "http://10.0.2.2:3000/auth",
userId = "username1-PCKid"
)
)
)
chatManager.connect(listeners = ChatListeners(
onErrorOccurred = { },
onAddedToRoom = { },
onRemovedFromRoom = { },
onCurrentUserReceived = { },
onNewReadCursor = { },
onRoomDeleted = { },
onRoomUpdated = { },
onPresenceChanged = { u, n, p -> },
onUserJoinedRoom = { u, r -> },
onUserLeftRoom = { u, r -> },
onUserStartedTyping = { u, r -> },
onUserStoppedTyping = { u, r -> }
)) { result ->
when (result) {
is Result.Success -> {
// We have connected!
val currentUser = result.value
AppController.currentUser = currentUser
val userJoinedRooms = ArrayList<Room>(currentUser.rooms)
for (i in 0 until userJoinedRooms.size) {
adapter.addRoom(userJoinedRooms[i]) // reads users rooms
}
currentUser.getJoinableRooms { result ->
when (result) {
is Result.Success -> {
// Do something with List<Room>
val rooms = result.value
runOnUiThread {
for (i in 0 until rooms.size) {
adapter.addRoom(rooms[i])
}
}
}
}
}
adapter.setInterface(object : ChatRoomsListAdapter.RoomClickedInterface {
override fun roomSelected(room: Room) {
if (room.memberUserIds.contains(currentUser.id)) {
// user already belongs to this room
roomJoined(room)
Log.d("roomSelected", "user already belongs to this room: " + roomJoined(room))
} else {
currentUser.joinRoom(
roomId = room.id,
callback = { result ->
when (result) {
is Result.Success -> {
// Joined the room!
roomJoined(result.value)
}
is Result.Failure -> {
Log.d("TAG", result.error.toString())
}
}
}
)
}
}
})
}
is Result.Failure -> {
// Failure
Log.d("TAG", result.error.toString())
}
}
}
}
private fun roomJoined(room: Room) {
val intent = Intent(this#ChatRoomsListActivity, ChatRoomActivity::class.java)
intent.putExtra("room_id", room.id)
intent.putExtra("room_name", room.name)
startActivity(intent)
}
}
ChatRoomsListAdapter
class ChatRoomsListAdapter: RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
private var list = ArrayList<Room>()
private var roomClickedInterface: RoomClickedInterface? = null
// lateinit var roomClickedInterface:RoomClickedInterface
fun addRoom(room:Room){
list.add(room)
notifyDataSetChanged()
// Log.d("Rooms", room.toString())
}
fun setInterface(roomClickedInterface:RoomClickedInterface){
this.roomClickedInterface = roomClickedInterface
// roomClickedInterface = roomClickedInterface
}
override fun getItemCount(): Int {
return list.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(
// R.layout.chat_list_row,
android.R.layout.simple_list_item_1,
parent,
false
)
return ViewHolder(view)
}
/*
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent!!.context)
.inflate(
android.R.layout.simple_list_item_1,
parent,
false
)
return ViewHolder(view)
}
*/
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.roomName.text = list[position].name
}
/*
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
holder!!.roomName.text = list[position].name
}
*/
inner class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView), View.OnClickListener {
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
Toast.makeText(itemView.context, "hello test", Toast.LENGTH_LONG).show();
Log.d("test", "testing console log")
}
var roomName: TextView = itemView.findViewById(android.R.id.text1)
init {
itemView.setOnClickListener(this)
}
}
interface RoomClickedInterface{
fun roomSelected(room:Room)
}
}
Rooms that should be shown in Recyclerview
1)
2)
this is what it looks like after i scroll any direction
3)
You need to ensure you are calling adapter.notifyDataSetChanged(). So change
currentUser.getJoinableRooms { result ->
when (result) {
is Result.Success -> {
// Do something with List<Room>
val rooms = result.value
runOnUiThread {
for (i in 0 until rooms.size) {
adapter.addRoom(rooms[i])
}
}
}
}
to
currentUser.getJoinableRooms { result ->
when (result) {
is Result.Success -> {
// Do something with List<Room>
val rooms = result.value
runOnUiThread {
for (i in 0 until rooms.size) {
adapter.addRoom(rooms[i])
}
adapter.notifyDataSetChanged()
}
}
}