firebase realtime database: messenger - android

I am building a chatting app using firebase realtime database.
I am storing the messages in both accounts simultaneously.
This is my fragment to send message:
class DmFragment : Fragment() {
lateinit var binding: FragmentDMBinding
lateinit var user: User
val list = ArrayList<Message>()
lateinit var auth: FirebaseAuth
lateinit var firebaseDatabase: FirebaseDatabase
lateinit var reference: DatabaseReference
lateinit var usersDMAdapter: UsersDMAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
user = it.getSerializable("user") as User
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentDMBinding.inflate(LayoutInflater.from(requireContext()), container, false)
auth = FirebaseAuth.getInstance()
val currentUser = auth.currentUser!!
firebaseDatabase = FirebaseDatabase.getInstance()
reference = firebaseDatabase.getReference("users")
binding.apply {
sendBtn.setOnClickListener {
val smsText = message.text.toString()
val simpleDateFormat = SimpleDateFormat("dd.MM.yyyy HH.ss")
val date = simpleDateFormat.format(Date())
val message1 = Message(smsText, date, currentUser.uid, user.uid)
val key = reference.push().key
reference.child("${currentUser.uid}/messages/${user.uid!!}/$key")
.setValue(message1)
reference.child("${user.uid}/messages/${currentUser.uid}/$key")
.setValue(message1)
message.text.clear()
}
reference.child("${currentUser.uid}/messages/${user.uid}")
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val children = snapshot.children
for (child in children) {
val value = child.getValue(Message::class.java)
if (value != null) {
list.add(value)
}
}
usersDMAdapter = UsersDMAdapter(list, currentUser.uid)
messageRv.adapter = usersDMAdapter
}
override fun onCancelled(error: DatabaseError) {
}
})
back.setOnClickListener {
findNavController().popBackStack()
}
userName.text = user.displayName
Picasso.get().load(user.photoUrl).into(userImage)
}
return binding.root
}
}
This is the adapter file that i used in the DMFragment:
class UsersDMAdapter(var list: List<Message>, var uid: String) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
inner class FromVh(val fromBinding: ItemFromBinding) :
RecyclerView.ViewHolder(fromBinding.root) {
fun onBind(message: Message) {
fromBinding.apply {
messageTv.text = message.message
messageDateTv.text = message.date
}
}
}
inner class ToVh(val toBinding: ItemToBinding) :
RecyclerView.ViewHolder(toBinding.root) {
fun onBind(message: Message) {
toBinding.apply {
messageTv.text = message.message
messageDateTv.text = message.date
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == 1) {
return FromVh(
ItemFromBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
} else {
return ToVh(
ItemToBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (getItemViewType(position)==1){
val fromVh = holder as FromVh
fromVh.onBind(list[position])
}else{
val toVh = holder as ToVh
toVh.onBind(list[position])
}
}
override fun getItemViewType(position: Int): Int {
return if (list[position].fromUid == uid) {
1
} else{
2
}
}
override fun getItemCount(): Int = list.size
}
This is my fragment that shows all users excluding myself:
class UserRecyclerFragment : Fragment() {
lateinit var binding: FragmentUserRecyclerBinding
lateinit var userRvAdapter: UserRvAdapter
var list = ArrayList<User>()
lateinit var auth: FirebaseAuth
lateinit var firebaseDatabase: FirebaseDatabase
lateinit var reference: DatabaseReference
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentUserRecyclerBinding.inflate(
LayoutInflater.from(requireContext()),
container,
false
)
auth = FirebaseAuth.getInstance()
val currentUser = auth.currentUser!!
firebaseDatabase = FirebaseDatabase.getInstance()
reference = firebaseDatabase.getReference("users")
val email = currentUser.email
val displayName = currentUser.displayName
val photo = currentUser.photoUrl
val phoneNumber = currentUser.phoneNumber
val uid = currentUser.uid
val user = User(
email,
displayName,
photo.toString(),
phoneNumber,
uid
)
reference.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
list.clear()
val filterList = arrayListOf<User>()
val children = snapshot.children
for (child in children) {
val value = child.getValue(User::class.java)
if (value != null && uid != value.uid) {
list.add(value)
}
if (value != null && value.uid == uid) {
filterList.add(value)
}
}
if (filterList.isEmpty()) {
reference.child(uid).setValue(user)
}
userRvAdapter = UserRvAdapter(list, object : UserRvAdapter.OnItemClickListener {
override fun onItemCick(user: User) {
val bundle = Bundle()
bundle.putSerializable("user", user)
findNavController().navigate(R.id.dmFragment, bundle)
}
})
binding.rv.adapter = userRvAdapter
}
override fun onCancelled(error: DatabaseError) {
}
})
return binding.root
}
}
Whenever i chat (send/receive) in DM, it is showing messages. But when I go back, messages that added in my user account is being deleted automatically. If i come back again, it is not showing the messages that have been saved in other person's account either. And i do not know what to do. Can anyone help?

Related

Can't get received messages

Somehow I see in the app messages I sent, but can't see those which should be received from others. Also when I'm sending a message it's sent to all users. Here I'm sending two classes, adapter and main chat activity. I tried using inner classes, different types of getItemViewType functions but nothing works.
MessageAdapter
class MessageAdapter(val context: Context, val messageList: ArrayList<Message>):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val ITEM_RECIEVE = 1
val ITEM_SENT = 2
class SentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
val sentMessage = itemView.findViewById<TextView>(R.id.txt_sent_message)
}
class RecieveViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
val recieveMessage = itemView.findViewById<TextView>(R.id.txt_recieve_message)
}
override fun getItemViewType(position: Int): Int {
val currentMessage = messageList[position]
if (FirebaseAuth.getInstance().currentUser?.uid.equals(currentMessage.senderId)) {
return ITEM_SENT
}
else {
return ITEM_RECIEVE
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == 1)
{
val view = LayoutInflater.from(context).inflate(R.layout.recieve, parent, false)
Log.d("tag2", "testrecieveviewholderview11")
return RecieveViewHolder(view)
}
else
{
val view = LayoutInflater.from(context).inflate(R.layout.sent, parent, false)
Log.d("tag", "testsentviewholderview22")
return SentViewHolder(view)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val currentMessage = messageList[position]
if(holder.javaClass == SentViewHolder::class.java)
{
val viewHolder = holder as SentViewHolder
holder.sentMessage.text = currentMessage.message
}
else
{
val viewHolder = holder as RecieveViewHolder
holder.recieveMessage.text = currentMessage.message
}
}
override fun getItemCount(): Int {
return messageList.size
}
}
And ChatActivity
var firebaseUser: FirebaseUser? = null
private lateinit var messageRecyclerView: RecyclerView
private lateinit var messageBox: EditText
private lateinit var sendButton: ImageButton
private lateinit var messageAdapter: MessageAdapter
private lateinit var messageList: ArrayList<Message>
private lateinit var mRef: DatabaseReference
var recieverRoom: String? = null
var senderRoom: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
val intent = Intent()
val name = intent.getStringExtra("name")
val recieverUid = intent.getStringExtra("uid")
val senderUid = FirebaseAuth.getInstance().currentUser?.uid
mRef = FirebaseDatabase.getInstance().reference
senderRoom = recieverUid + senderUid
recieverRoom = senderUid + recieverUid
messageRecyclerView = findViewById(R.id.RecyclerViewChat)
messageBox = findViewById(R.id.messageBox)
sendButton = findViewById(R.id.sendButton)
messageList = ArrayList()
messageAdapter = MessageAdapter(this, messageList)
messageRecyclerView.layoutManager = LinearLayoutManager(this)
messageRecyclerView.scrollToPosition(messageAdapter.itemCount-1)
messageRecyclerView.adapter = messageAdapter
mRef.child("chats").child(senderRoom!!).child("messages")
.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
messageList.clear()
for (postSnapshot in snapshot.children)
{
val message = postSnapshot.getValue(Message::class.java)
messageList.add(message!!)
}
messageAdapter.notifyDataSetChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
sendButton.setOnClickListener {
if (messageBox.text.toString().isEmpty() || messageBox.text.toString().isBlank())
{
messageBox.setText("")
}
else {
val message = messageBox.text.toString()
val messageObject = Message(message, senderUid)
mRef.child("chats").child(senderRoom!!).child("messages").push()
.setValue(messageObject).addOnSuccessListener {
mRef.child("chats").child(recieverRoom!!).child("messages").push()
.setValue(messageObject)
}
messageBox.setText("")
}
}
}
}
The error coming in this code is "Unresolved reference: message" and "Unresolved reference: senderId" in MessageAdapter. MessageAdapter is not able to take variables that are declared in Message class(message and senderId).

Clicking on Recyclerview item has a late item

I have an application using databinding, livedata, room, kotlin koroutines, viewmodel, retrofit and koin. I have one activity, and two fragments.
UserListFragment: Show in a recyclerview a list of user items.
UserFullProfileFragment: Show the user item detail.
When the application is running, an external API is called to retrieve a list of users and display it in a recyclerview. Then, if I click on one item, an external API is called to get the detail of the current user whith its ID.
The problem is when I click on one item at the first, everything is going well but for following items, this is the detail of previous item which is displayed and so on and so forth.
Any ideas ?
UserRepository:
class UserRepositoryImpl (private val userApi: UserApi, private val userDao: UserDao, private val networkStateManager: NetworkStateManager) : UserRepository {
override suspend fun getUserList(): Result<List<UserListItem>> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user list from user API
val response = userApi.getUserList()
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$response")
val userList = userResponse.data
// convert user API object to user entity
val entities = userList.map { it.toUserEntity() }
// save user list in database
withContext(Dispatchers.IO) { userDao.addUsers(entities) }
// convert user entity to user model
val userItemList = entities.map { it.toUserListItem() }
return Result.Success(userItemList)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user list from database if no network
val data = withContext(Dispatchers.IO) { userDao.findAllUsers() }
return if (data.isNotEmpty()) {
Log.d("REPO", "get users from db")
val userItemList = data.map { it.toUserListItem() }
Result.Success(userItemList)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
override suspend fun getUserFullProfile(userId: String): Result<UserFullProfile> {
if (networkStateManager.hasNetWorkConnection()) {
return try {
// get user from user API
val response = userApi.getUserFullProfile(userId)
if (response.isSuccessful) {
Log.d("REPO", "get users from api")
response.body()?.let { userResponse ->
Log.d("REPO", "response:$userResponse")
// convert user API object to user entity
val userEntity = userResponse.toUserEntity()
// save user data in database
withContext(Dispatchers.IO) { userDao.addUserFullProfile(userEntity) }
// convert user entity to user model
val user = userEntity.toUserFullProfile()
return Result.Success(user)
} ?: handleFailure(response)
} else {
handleFailure(response)
}
} catch (e: Exception) {
return Result.Failure(e, e.localizedMessage)
}
} else {
// get user from database if no network
val data = withContext(Dispatchers.IO) { userDao.getUserById(userId) }
return if (data != null) {
Log.d("REPO", "get users from db")
val user = data.toUserFullProfile()
Result.Success(user)
} else {
Result.Failure(Exception("error"), "no network connection")
}
}
}
UserViewModel:
getUserList and getUserFullProfile are use cases which call the repository
class UserViewModel (private val getUserList: GetUserList, private val getUserFullProfile: GetUserFullProfile) : ViewModel() {
val userList = MutableLiveData<List<UserListItem>>()
val userFullProfile = MutableLiveData<UserFullProfile>()
fun getUserList() {
viewModelScope.launch {
when (val result = getUserList.getUserList()) {
is Result.Success -> userList.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
fun getUserFullProfile(userId: String) {
viewModelScope.launch {
when (val result = getUserFullProfile.getUserFullProfile(userId)) {
is Result.Success -> userFullProfile.value = result.successData
is Result.Failure -> result.exception.localizedMessage
}
}
}
UserRecyclerAdaper:
class UserRecyclerAdapter(private val context: Context?, val clickListener: UserClickListener) : RecyclerView.Adapter<UserRecyclerAdapter.UserViewHolder>() {
var userList : List<UserListItem> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflatedView: UserItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.user_item, parent, false)
return UserViewHolder(inflatedView)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bindUser(position)
}
override fun getItemCount() = userList.size
fun setUsers(users: List<UserListItem>) {
this.userList = users
notifyDataSetChanged()
}
inner class UserViewHolder(private val v: UserItemBinding) : RecyclerView.ViewHolder(v.root) {
fun bindUser(position: Int) {
val item = userList[position]
Log.d("ADAPTER", item.toString())
v.user = item
Picasso.get()
.load(item.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(v.picture)
v.userClickInterface = clickListener
v.root.setOnClickListener {
clickListener.onItemClick(item)
}
}
}
UserListFragment:
class UserListFragment : Fragment(), UserClickListener {
private val userViewModel by viewModel<UserViewModel>()
private lateinit var userAdapter: UserRecyclerAdapter
private lateinit var viewDataBinding: FragmentUserListBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
viewDataBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_user_list, container, false)
viewDataBinding.lifecycleOwner = this
return viewDataBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
userAdapter = UserRecyclerAdapter(context, this)
recyclerView.adapter = userAdapter
recyclerView.isNestedScrollingEnabled = false
viewDataBinding.viewModel = userViewModel
userViewModel.getUserList()
userViewModel.userList.observe(viewLifecycleOwner, { userList ->
if (userList.isNotEmpty() && userList != null) {
userAdapter.setUsers(userList)
}
})
}
override fun onItemClick(user: UserListItem) {
Log.d("FRAGMENT", user.toString())
userViewModel.getUserFullProfile(user.id)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
Log.d("UFP", userFullProfile.toString())
if (userFullProfile != null) {
(activity as MainActivity).replaceFragment(UserFullProfileFragment.newInstance(userFullProfile),
R.id.fragment_layout, "userFullProfile")
}
})
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
companion object {
#JvmStatic
fun newInstance(user: UserFullProfile) = UserFullProfileFragment().apply {
arguments = Bundle().apply {
putParcelable("user", user)
}
}
}
private var user: UserFullProfile? = null
private lateinit var mViewDataBinding: FragmentUserFullProfileBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewDataBinding.user = user
notify()
Picasso.get()
.load(mViewDataBinding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(mViewDataBinding.picture)
val dateOfBirth = parseDate(mViewDataBinding.user?.dateOfBirth)
mViewDataBinding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(mViewDataBinding.user?.registerDate)
mViewDataBinding.registerDate.text = registerDate
}
override fun onAttach(context: Context) {
super.onAttach(context)
user = arguments?.getParcelable("user")
Log.d("APP", user.toString())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
mViewDataBinding.lifecycleOwner = this
return mViewDataBinding.root
}
Thank you :)
Finally, I found a solution :
I pass the user id argment from UserListFragment to UserFullProfileFragment instead of the current user object and I call the external API to get the current user in the UserFullProfileFragment.
This is the final code:
UserListFragment:
override fun onItemClick(user: UserListItem) {
val action = UserListFragmentDirections.actionUserListFragmentToUserFullProfileFragment(user.id)
findNavController().navigate(action)
}
UserFullProfileFragment:
class UserFullProfileFragment : Fragment() {
private lateinit var userID: String
private var mViewDataBinding: FragmentUserFullProfileBinding? = null
private val binding get() = mViewDataBinding!!
private val userViewModel by viewModel<UserViewModel>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.viewModel = userViewModel
userID = UserFullProfileFragmentArgs.fromBundle(requireArguments()).userArgs
userViewModel.getUserFullProfile(userID)
userViewModel.userFullProfile.observe(viewLifecycleOwner, { userFullProfile ->
if (userFullProfile != null) {
binding.user = userFullProfile
Picasso.get()
.load(binding.user?.picture)
.placeholder(R.drawable.ic_launcher_foreground)
.error(R.drawable.ic_launcher_background)
.into(binding.picture)
val dateOfBirth = parseDate(binding.user?.dateOfBirth)
binding.dateOfBirth.text = dateOfBirth
val registerDate = parseDate(binding.user?.registerDate)
binding.registerDate.text = registerDate
}
})
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mViewDataBinding = DataBindingUtil.inflate(inflater,
R.layout.fragment_user_full_profile, container, false)
binding.lifecycleOwner = this
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
mViewDataBinding = null
}
}

Get user ID's in a pre defined order (Firebase & Kotlin)

UPDATED QUESTION
I have an ordered List of Chats (userMsglist), but since I need to show the User Profile Image and Name associated with each chat, I have to create a mUsers list based on the Users node in the FB Realtime DB. At this point, I loose the order present. How do I create a mUsers list based on the order in userMsgList
private fun retrieveMessageList()
{
mUsers = ArrayList()
val ref = FirebaseDatabase.getInstance().reference.child("Users")
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
(mUsers as ArrayList).clear()
// usersMsgList?.sortedBy { it.getChattimeStmp() }
//Sorting of chatlist
Collections.sort(usersMsgList!!, this#Messages::compare)
for (dataSnapshot in p0.children) {
val user = dataSnapshot.getValue(Users::class.java)
for (eachMessageList in usersMsgList!!) {
if(eachMessageList.getId().equals(user!!.getUID()) && !firebaseUser?.uid.equals(eachMessageList.getId())) {
(mUsers as ArrayList).add(user)
}
}
}
userMsgAdapter = UserMsgAdapter(context!!, (mUsers as ArrayList<Users>), true)
recycler_view_msgList.adapter = userMsgAdapter
}
override fun onCancelled(p0: DatabaseError) {
}
}
)
}
private fun compare(o1: MessageList, o2: MessageList): Int {
return if (o1.getChattimeStmp()!! < o2.getChattimeStmp()!!) 1 else if (o1.getChattimeStmp() == o2.getChattimeStmp()) 0 else -1
}
The image of for the structure of Chatlist is as below:
The image for the structure of the User is below:
The full code is below
class Messages : Fragment() {
private var userMsgAdapter: UserMsgAdapter? = null
private var mUsers: List<Users>? = null
private var usersMsgList: List<MessageList>? = null
private var firebaseUser : FirebaseUser? = null
lateinit var recycler_view_msgList : RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_messages, container, false)
recycler_view_msgList = view.findViewById(R.id.recycler_view_msglist)
recycler_view_msgList.setHasFixedSize(true)
recycler_view_msgList.layoutManager = LinearLayoutManager(context)
firebaseUser = FirebaseAuth.getInstance().currentUser
usersMsgList = ArrayList()
val ref = FirebaseDatabase.getInstance().reference.child("ChatList").child((firebaseUser!!.uid))
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
(usersMsgList as ArrayList).clear()
for (dataSnapshot in p0.children) {
val messageList = dataSnapshot.getValue(MessageList::class.java)
(usersMsgList as ArrayList).add(messageList!!)
}
retrieveMessageList()
}
override fun onCancelled(p0: DatabaseError) {
}
})
updateToken(FirebaseInstanceId.getInstance().token)
return view
}
private fun updateToken(token: String?)
{
val ref = FirebaseDatabase.getInstance().reference.child("Tokens")
val Token1 = Token(token!!)
ref.child(firebaseUser!!.uid).setValue(Token1)
}
private fun retrieveMessageList()
{
mUsers = ArrayList()
val ref = FirebaseDatabase.getInstance().reference.child("Users")
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
(mUsers as ArrayList).clear()
// usersMsgList?.sortedBy { it.getChattimeStmp() }
//Sorting of chatlist
Collections.sort(usersMsgList!!, this#Messages::compare)
for (dataSnapshot in p0.children) {
val user = dataSnapshot.getValue(Users::class.java)
for (eachMessageList in usersMsgList!!) {
if(eachMessageList.getId().equals(user!!.getUID()) && !firebaseUser?.uid.equals(eachMessageList.getId())) {
(mUsers as ArrayList).add(user)
}
}
}
userMsgAdapter = UserMsgAdapter(context!!, (mUsers as ArrayList<Users>), true)
recycler_view_msgList.adapter = userMsgAdapter
}
override fun onCancelled(p0: DatabaseError) {
}
}
)
}
private fun compare(o1: MessageList, o2: MessageList): Int {
return if (o1.getChattimeStmp()!! < o2.getChattimeStmp()!!) 1 else if (o1.getChattimeStmp() == o2.getChattimeStmp()) 0 else -1
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
messages_fab.setOnClickListener{
val intent = Intent(context, MyContacts::class.java)
startActivity(intent)
}
}
}

Can we retrieve firebase data into spinner?

I have added firebase database and the data also gets saved into the firebase realtime database, but it doesnot gets shown into the spinner, my spinner is showing no value.If theres some other way?
This is the code of my fragment:
class IncomeFragment: Fragment(){
//FAB
lateinit var addBankFab: FloatingActionButton
lateinit var addBankText: TextView
//Spinner
lateinit var spinner: Spinner
lateinit var dropDown: TextView
//Firebase Database
lateinit var mAuth: FirebaseAuth
lateinit var mIncomeDatabase: DatabaseReference
lateinit var mBankDatabase: DatabaseReference
lateinit var helper: FirebaseHelper
//Recycler View
lateinit var recyclerView: RecyclerView
//TextView
lateinit var incomeTotal : TextView
//Update Edit Text
lateinit var editAmount: EditText
lateinit var editType: EditText
lateinit var editNote: EditText
lateinit var editBankName:EditText
//Button for update and delete
lateinit var btnUpdate: Button
lateinit var btnDelete: Button
lateinit var btnSave : Button
lateinit var btnCancel:Button
//Data item Value
lateinit var type: String
lateinit var note: String
var amount: Int = 0
lateinit var post_key:String
lateinit var name:String
lateinit var ac:String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val myview= inflater.inflate(R.layout.fragment_income, container, false)
mAuth= FirebaseAuth.getInstance()
val mUser = mAuth.currentUser
val uid = mUser?.uid
addBankFab=myview.findViewById(R.id.add_bank_fab)
addBankText=myview.findViewById(R.id.add_bank_text)
spinner=myview.findViewById(R.id.incomeSpinner)
mIncomeDatabase =
FirebaseDatabase.getInstance().reference.child("IncomeData").child(uid.toString())
incomeTotal=myview.findViewById(R.id.income_txt_result)
mBankDatabase= FirebaseDatabase.getInstance().reference.child("BankData").child(uid.toString())
helper = FirebaseHelper(mBankDatabase)
spinner.setAdapter(
activity?.let {
ArrayAdapter<String>(
it,
android.R.layout.simple_list_item_1,
helper.retrieve()
)
}
)
recyclerView=myview.findViewById(R.id.incomeRecyclerView)
val layoutManager = LinearLayoutManager(activity)
layoutManager.reverseLayout = true
layoutManager.stackFromEnd = true
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = layoutManager
mIncomeDatabase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
var totalvalue :Int = 0
for (ds in dataSnapshot.children) {
val data: Data = ds.getValue<Data>(Data::class.java)!!
totalvalue+=data.amount
val stTotalValue:String = valueOf(totalvalue)
incomeTotal.text = stTotalValue+".00"
}
}
override fun onCancelled(databaseError: DatabaseError) {}
})
return myview
}
private fun insertBank() {
val bankdialog = AlertDialog.Builder(activity)
val inflater = LayoutInflater.from(activity)
val view: View = inflater.inflate(R.layout.add_bank_layout, null)
bankdialog.setView(view)
val dialog = bankdialog.create()
dialog.setCancelable(false)
val BankName = view.findViewById<EditText>(R.id.account_bank)
val BankAc = view.findViewById<EditText>(R.id.account_number)
val btnSaveBank = view.findViewById<Button>(R.id.btn_save_bank)
val btnCancelBank = view.findViewById<Button>(R.id.btn_cancel_bank)
btnSaveBank.setOnClickListener {
val name = BankName.text.toString().trim()
val ac = BankAc.text.toString().trim()
if (TextUtils.isEmpty(name)) {
BankName.error = "Required Field..."
return#setOnClickListener
}
if (TextUtils.isEmpty(ac)) {
BankAc.error = "Required Field..."
return#setOnClickListener
}
val bankacno = ac.toInt()
val id: String? = mBankDatabase.push().key
val mDate: String = DateFormat.getDateInstance().format(Date())
val bankdata = EmiData(bankacno, name, id, mDate)
mBankDatabase.child(id.toString()).setValue(bankdata)
Toast.makeText(activity, "DATA ADDED", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
btnCancelBank.setOnClickListener {
dialog.dismiss()
}
dialog.show()
}
override
fun onStart() {
super.onStart()
val options = FirebaseRecyclerOptions.Builder<Data>()
.setQuery(mIncomeDatabase, Data::class.java)
.setLifecycleOwner(this)
.build()
val firebaseRecyclerAdapter: FirebaseRecyclerAdapter<Data, MyViewHolder> =
object : FirebaseRecyclerAdapter<Data, MyViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.income_recycler_data, parent, false)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int, model: Data) {
holder.setType(model.type)
holder.setAmount(model.amount)
holder.setDate(model.date)
holder.setNote(model.note)
holder.mView.setOnClickListener {
post_key= getRef(position).key.toString()
type=model.type
note=model.note
amount=model.amount
updateDataItem()
}
}
}
recyclerView.adapter = firebaseRecyclerAdapter
}
class MyViewHolder(itemView: View) : ViewHolder(itemView) {
val mView = itemView
fun setType(type: String) {
val mType = mView.findViewById<TextView>(R.id.type_txt_income)
mType.text = type
}
fun setNote(note: String) {
val mNote = mView.findViewById<TextView>(R.id.note_txt_income)
mNote.text = note
}
fun setDate(date: String) {
val mDate = mView.findViewById<TextView>(R.id.date_txt_income)
mDate.text = date
}
fun setAmount(amount: Int) {
val mAmount = mView.findViewById<TextView>(R.id.amount_txt_income)
val stAmount = valueOf(amount)
mAmount.text = stAmount
}
}
private fun updateDataItem() {
val mydialog = AlertDialog.Builder(activity)
val inflater = LayoutInflater.from(activity)
val myView: View = inflater.inflate(R.layout.update_data_item, null)
mydialog.setView(myView)
val dialog = mydialog.create()
val editAmount = myView.findViewById<EditText>(R.id.amount_edit)
val editType = myView.findViewById<EditText>(R.id.type_edit)
val editNote = myView.findViewById<EditText>(R.id.note_edit)
//Set data to edit text..
editType.setText(type)
editType.setSelection(type.length)
editNote.setText(note)
editNote.setSelection(note.length)
editAmount.setText(valueOf(amount))
editAmount.setSelection(valueOf(amount).length)
val btnUpdate = myView.findViewById<Button>(R.id.btn_update)
val btnDelete = myView.findViewById<Button>(R.id.btn_delete)
btnUpdate.setOnClickListener {
type = editType.text.toString().trim()
note = editNote.text.toString().trim()
var mdAmount = amount.toString()
mdAmount = editAmount.text.toString().trim { it <= ' ' }
val myAmount = mdAmount.toInt()
val mDate: String = DateFormat.getDateInstance().format(Date())
val data = Data(myAmount, type, note, post_key, mDate)
mIncomeDatabase.child(post_key).setValue(data)
dialog.dismiss()
}
btnDelete.setOnClickListener {
mIncomeDatabase.child(post_key).removeValue()
dialog.dismiss()
}
dialog.show()
}
}
This is my firebase helper:
class FirebaseHelper(val db: DatabaseReference) {
private var saved: Boolean? = null
//SAVE
fun save(bankData: BankData?): Boolean? {
saved = if (bankData == null) {
false
} else {
try {
db.child("name").push().setValue(bankData)
true
} catch (e: DatabaseException) {
e.printStackTrace()
false
}
}
return saved
}
//READ
fun retrieve(): ArrayList<String> {
val bankNames = ArrayList<String>()
db.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(
dataSnapshot: DataSnapshot,
s: String?
) {
fetchData(dataSnapshot, bankNames)
}
override fun onChildChanged(
dataSnapshot: DataSnapshot,
s: String?
) {
fetchData(dataSnapshot, bankNames)
}
override fun onChildRemoved(dataSnapshot: DataSnapshot) {}
override fun onChildMoved(
dataSnapshot: DataSnapshot,
s: String?
) {
}
override fun onCancelled(databaseError: DatabaseError) {}
})
return bankNames
}
private fun fetchData(
snapshot: DataSnapshot,
bankData: ArrayList<String>
) {
bankData.clear()
val name: BankData? = snapshot.getValue(BankData::class.java)
bankData.add(bankData.toString())
}
}
Your spinner shows no value because the data is not retrieved yet from the DB.
So what you should do is create a callback from FirebaseHelper to IncomeFragment
First, create a callback
interface FirebaseHelperCallback {
fun dataRetrievedFromDB(data: List<String>)
}
Second, create a variable, setter function and call dataRetrievedFromDB function in FirebaseHelper class
class FirebaseHelper(val db: DatabaseReference) {
private lateinit var firebaseHelperCallback: FirebaseHelperCallback
fun setFirebaseHelperCallback(firebaseHelperCallback: FirebaseHelperCallback) {
this.firebaseHelperCallback = firebaseHelperCallback
}
...
private fun fetchData(snapshot: DataSnapshot, bankData: ArrayList<String>) {
bankData.clear()
val name: BankData? = snapshot.getValue(BankData::class.java)
bankData.add(bankData.toString())
firebaseHelperCallback.dataRetrievedFromDB(bankData)
}
}
Lastly, implement the callback in IncomeFragment
class IncomeFragment: Fragment(), FirebaseHelperCallback {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
...
helper.setFirebaseHelperCallback(this)
}
...
override fun dataRetrievedFromDB(data: List<String>) {
spinner.setAdapter(
activity?.let {
ArrayAdapter<String>(
it,
android.R.layout.simple_list_item_1,
data
)
}
)
}
}

Update current list item

I open the list item, get the data in fetchData(), then I expect that by calling the addTarget() method I will update the current item(name and description). Instead, I create a new one.
Q: How can I update the current one?
class TargetEditFragment : Fragment() {
private var nameEditText: TextInputEditText? = null
private var descriptionEditText: TextInputEditText? = null
private var button: Button? = null
private var databaseReference: DatabaseReference? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.getString(KEY_TARGET_GUID, "")
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_target_add, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
databaseReference = FirebaseDatabase.getInstance().getReference("targets")
setupViews()
fetchData(guid = arguments?.getString(KEY_TARGET_GUID, "") ?: "")
}
private fun setupViews() {
nameEditText = view?.findViewById(R.id.nameEditText)
descriptionEditText = view?.findViewById(R.id.descriptionEditText)
button = view?.findViewById(R.id.addNote)
button?.setOnClickListener { addTarget() }
}
private fun addTarget() {
val name = nameEditText?.text.toString().trim()
val description = descriptionEditText?.text.toString().trim()
if (!TextUtils.isEmpty(name)) {
val id: String = databaseReference?.push()?.key.toString()
val target = Target(guid = id, name = name, description = description)
databaseReference?.child(id)?.setValue(target)
} else Log.d("some", "Enter a name")
}
private fun fetchData(guid: String) {
// Attach a listener to read the data at the target id
databaseReference?.child(guid)?.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val data = dataSnapshot.value as HashMap<String, String>
val name = data["name"] ?: ""
val description = data["description"] ?: ""
if (name.isEmpty()) Log.d("some", "nameIsEmpty")
else {
updateViewsContent(name = name, description = description)
}
}
override fun onCancelled(p0: DatabaseError) {
Log.d("some", "onCancelled")
}
})
}
private fun updateViewsContent(name: String?, description: String?) {
nameEditText?.text = Editable.Factory.getInstance().newEditable(name)
descriptionEditText?.text = Editable.Factory.getInstance().newEditable(description)
}
companion object {
fun newInstance(guid: String): TargetEditFragment =
TargetEditFragment().apply {
arguments = Bundle().apply { putString(KEY_TARGET_GUID, guid) }
}
}
}
There is no way to update an element using the push() method because everytime you call this method a new unique key is generated. In order to perform an update you need to know the key of the element you want to update and use it in your reference. For more informations, please see my answer from the following post:
How to get specific pushedID in Firebase?

Categories

Resources