How to save LocalData in Firebase Realtime database? - firebase

I am very new using Kotlin and programming and I am currently making a calendar with events. My problem comes when I want to connect these events to firebase.
I am using an example that I found in git (https://github.com/kizitonwose/CalendarView) that uses the ThreeTen library for dates. This is the Event object:
class Event (val id: String, val text: String, val date: LocalDate) : Serializable
The data variable is of the LocalData type and this is what is causing me problems since it seems that Firebase only accepts variables of type String, Int, etc ...
I tried to pass the variable to String with toString and with Gson (), without success.
Here is the code if it helps
private val inputDialog by lazy {
val editText = AppCompatEditText(requireContext())
val layout = FrameLayout(requireContext()).apply {
// Setting the padding on the EditText only pads the input area
// not the entire EditText so we wrap it in a FrameLayout.
setPadding(20, 20, 20, 20)
addView(editText, FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
}
AlertDialog.Builder(requireContext())
.setTitle(getString(R.string.example_3_input_dialog_title))
.setView(layout)
.setPositiveButton(R.string.save) { _, _ ->
saveEvent(editText.text.toString())
// Prepare EditText for reuse.
editText.setText("")
}
.setNegativeButton(R.string.close, null)
.create()
.apply {
setOnShowListener {
// Show the keyboard
editText.requestFocus()
context.inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
}
setOnDismissListener {
// Hide the keyboard
context.inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
}
}
private var selectedDate: LocalDate? = null
private val today = LocalDate.now()
private val titleSameYearFormatter = DateTimeFormatter.ofPattern("MMMM")
private val titleFormatter = DateTimeFormatter.ofPattern("MMM yyyy")
private val selectionFormatter = DateTimeFormatter.ofPattern("yyyy MM dd")
private val events = mutableMapOf<LocalDate, List<Event>>()
private var prueba = Gson().toJson(events)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_calendar, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mDatabaseReference = mDatabase!!.reference.child("events")
exThreeRv.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.VERTICAL, false)
exThreeRv.adapter = eventsAdapter
exThreeRv.addItemDecoration(DividerItemDecoration(requireContext(), RecyclerView.VERTICAL))
val daysOfWeek = daysOfWeekFromLocale()
val currentMonth = YearMonth.now()
exThreeCalendar.setup(currentMonth.minusMonths(10), currentMonth.plusMonths(10), daysOfWeek.first())
exThreeCalendar.scrollToMonth(currentMonth)
if (savedInstanceState == null) {
exThreeCalendar.post {
// Show today's events initially.
selectDate(today)
}
}
class DayViewContainer(view: View) : ViewContainer(view) {
lateinit var day: CalendarDay // Will be set when this container is bound.
val textView = view.exThreeDayText
val dotView = view.exThreeDotView
init {
view.setOnClickListener {
if (day.owner == DayOwner.THIS_MONTH) {
selectDate(day.date)
}
}
}
}
exThreeCalendar.dayBinder = object : DayBinder<DayViewContainer> {
override fun create(view: View) = DayViewContainer(view)
override fun bind(container: DayViewContainer, day: CalendarDay) {
container.day = day
val textView = container.textView
val dotView = container.dotView
textView.text = day.date.dayOfMonth.toString()
if (day.owner == DayOwner.THIS_MONTH) {
textView.makeVisible()
when (day.date) {
today -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.today_bg)
dotView.makeInVisible()
}
selectedDate -> {
textView.setTextColorRes(R.color.white)
textView.setBackgroundResource(R.drawable.selected_bg)
dotView.makeInVisible()
}
else -> {
textView.setTextColorRes(R.color.black)
textView.background = null
dotView.isVisible = events[day.date].orEmpty().isNotEmpty()
}
}
} else {
textView.makeInVisible()
dotView.makeInVisible()
}
}
}
exThreeCalendar.monthScrollListener = {
requireActivity().home.text = if (it.year == today.year) {
titleSameYearFormatter.format(it.yearMonth)
} else {
titleFormatter.format(it.yearMonth)
}
// Select the first day of the month when
// we scroll to a new month.
selectDate(it.yearMonth.atDay(1))
}
class MonthViewContainer(view: View) : ViewContainer(view) {
val legendLayout = view.legendLayout
}
exThreeCalendar.monthHeaderBinder = object : MonthHeaderFooterBinder<MonthViewContainer> {
override fun create(view: View) = MonthViewContainer(view)
override fun bind(container: MonthViewContainer, month: CalendarMonth) {
// Setup each header day text if we have not done that already.
if (container.legendLayout.tag == null) {
container.legendLayout.tag = month.yearMonth
container.legendLayout.children.map { it as TextView }.forEachIndexed { index, tv ->
tv.text = daysOfWeek[index].name.first().toString()
tv.setTextColorRes(R.color.black)
}
}
}
}
exThreeAddButton.setOnClickListener {
inputDialog.show()
}
}
private fun selectDate(date: LocalDate) {
if (selectedDate != date) {
val oldDate = selectedDate
selectedDate = date
oldDate?.let { exThreeCalendar.notifyDateChanged(it) }
exThreeCalendar.notifyDateChanged(date)
updateAdapterForDate(date)
}
}
private fun saveEvent(text: String) {
if (text.isBlank()) {
Toast.makeText(requireContext(),
R.string.example_3_empty_input_text, Toast.LENGTH_LONG).show()
} else {
selectedDate?.let {
events[it] = events[it].orEmpty().plus(
Event(
UUID.randomUUID().toString(),
text,
it
)
)
uploadFirebase()
updateAdapterForDate(it)
}
}
}
private fun deleteEvent(event: Event) {
val date = event.date
events[date] = events[date].orEmpty().minus(event)
updateAdapterForDate(date)
}
private fun updateAdapterForDate(date: LocalDate) {
eventsAdapter.events.clear()
eventsAdapter.events.addAll(events[date].orEmpty())
eventsAdapter.notifyDataSetChanged()
exThreeSelectedDateText.text = selectionFormatter.format(date)
}
fun uploadFirebase(){
val newEvent = mDatabaseReference.push()
newEvent.setValue(events)
}
override fun onStart() {
super.onStart()
}
override fun onStop() {
super.onStop()
}
}

There is no way you can add a property of type LocalDate in a Firebase Realtime database because it is not a supported data-type. However, there are two ways in which you can solve this:
You save the date as a ServerValue.TIMESTAMP, which basically means that you save the number of seconds that have elapsed since the Unix epoch. In this case, the server writes the current date in the database. To achieve this, please see my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
You specify a custom long value for your date field. In this case, it's up to you to determine what date is written.
Unfortunately, there is no way you can combine these two options, you can use one or the other.
When talking about a LocalDate, we usually talk about an offset, in which case, this what I'll do. I'll store a Timestamp property, as explained at point one, that will let the server populate with the server Timestamp, as well as an offset property, that should be populated with the offset in days/hours.

Related

LazyColumn not update when data in LiveData update

I want to create chat app UI using ViewModel but when I send button the List in ViewModel update but LayzColumn not update it data. I don't know why it's not working.
My ChatViewModel
class ChatViewModel: ViewModel() {
private val _messages: MutableLiveData<MutableList<String>> = MutableLiveData(mutableListOf(""))
val messages: LiveData<MutableList<String>> get() = _messages
fun add(message: String) {
_messages.value?.add(message)
Log.d("Haha", "${_messages.value?.size}")
_messages.notifyObserver()
}
}
fun <T> MutableLiveData<T>.notifyObserver() {
this.value = value
}
My LazyColumn
val messages by chatViewModel.messages.observeAsState(mutableListOf(""))
LazyColumn(modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
items(messages.toList()) { message ->
SenderChat(message = message)
}
}

avoid fragment recreation BottomNavigationView

i want to avoid fragment recreation by using BottomNavigationView
i read about that FragmentTransaction.replace is the problem and changing to add would help, but that didn't work for me..
maybe you can see here where i'm wrong
this is my Host Activity which hosting 3 fragments with BottomNavigationView
'''
class Host : AppCompatActivity() {
var fragment: Fragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_host)
//for hide the actionBar (contains Fragment name) on top of the screen
val actionBar = supportActionBar
actionBar?.hide()
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
val navView = findViewById<BottomNavigationView>(R.id.nav_view)
navView?.setupWithNavController(navController)
NavigationUI.setupWithNavController(navView, navController)
mAdapter = NfcAdapter.getDefaultAdapter(this)
fragmentStayAlive()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
for (fragment in supportFragmentManager.fragments) {
fragment.onActivityResult(requestCode, resultCode, data)
}
}
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
private fun fragmentStayAlive(){
val fragment1: Fragment = LobbyFragment()
val fragment2: Fragment = GameSessionFragment()
val fragment3: Fragment = UserStatusFragment()
val fm: FragmentManager = supportFragmentManager
var active = fragment1
fm.beginTransaction().add(R.id.nav_host_fragment, fragment3, "UserStatusFragment").hide(
fragment3
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment2, "GameSessionFragment").hide(
fragment2
).commit();
fm.beginTransaction().add(R.id.nav_host_fragment, fragment1, "LobbyFragment").commit();
val mOnNavigationItemSelectedListener: BottomNavigationView.OnNavigationItemSelectedListener =
object : BottomNavigationView.OnNavigationItemSelectedListener {
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.getItemId()) {
R.id.lobbyFragment -> {
fm.beginTransaction().hide(active).show(fragment1).commit()
active = fragment1
return true
}
R.id.gameSessionFragment -> {
fm.beginTransaction().hide(active).show(fragment2).commit()
active = fragment2
return true
}
R.id.userStatusFragment -> {
fm.beginTransaction().hide(active).show(fragment3).commit()
active = fragment3
return true
}
}
replaceFragment(active, null, true, true)
return false
}
}
}
fun replaceFragment(
fragment: Fragment,
#Nullable bundle: Bundle?,
popBackStack: Boolean,
findInStack: Boolean
) {
val fm = supportFragmentManager
val ft: FragmentTransaction = fm.beginTransaction()
val tag = fragment.javaClass.name
val parentFragment: Fragment?
parentFragment = if (findInStack && fm.findFragmentByTag(tag) != null) {
fm.findFragmentByTag(tag)
} else {
fragment
}
// if user passes the #bundle in not null, then can be added to the fragment
if (bundle != null) {
parentFragment!!.arguments = bundle
} else {
parentFragment!!.arguments = null
}
// this is for the very first fragment not to be added into the back stack.
if (popBackStack) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
ft.addToBackStack(parentFragment.javaClass.name + "")
}
ft.add(R.id.nav_view, parentFragment, tag)
ft.commit()
fm.executePendingTransactions()
}
'''
thanks for all the helpers!

Kotlin searchView does not reload Firebase array

I have one activity with 2 recyclerViews, both recyclers are used with Firebase. One of the recyclers displays the results of a query, the other recycler has a listener that updates every time there is an update in Firebase. I added a searchView to filter the results from Firebase. The issue I'm having is when I'm trying to search the results from the Firebase query, when I start typing I see results but when I click the X to stop searching, the adapter does not reload the array and i don't see the list of items unless I reload the activity. I'm not sure what I'm missing here. Any help/suggestion is greatly appreciated. Here is my code:
Adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.eduardoz.ezmdapp.Model.Charges
import com.eduardoz.ezmdapp.R
class ChargesAdapter (private var charges: ArrayList<Charges>
, private var chargesAll: ArrayList<Charges>
, private val itemClick: (Charges) -> Unit)
: RecyclerView.Adapter<ChargesAdapter.ViewHolder>()
, Filterable {
inner class ViewHolder(itemView: View, val itemClick: (Charges) -> Unit) :
RecyclerView.ViewHolder(itemView) {
private val chargeCode = itemView.findViewById<TextView>(R.id.chargeCodeTxt)
private val chargeDescription = itemView.findViewById<TextView>(R.id.chargeDescriptionTxt)
fun bindCharges(charges: Charges) {
chargeCode?.text = charges.chargeCode
chargeDescription?.text = charges.chargeDescription
itemView.setOnClickListener { itemClick(charges) }
}
}
init {
this.charges = charges
chargesAll = java.util.ArrayList(charges)
}
override fun getItemCount(): Int {
return charges.count()
}
override fun onBindViewHolder(holder: ChargesAdapter.ViewHolder, position: Int) {
holder.bindCharges(charges[position])
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChargesAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.charges_list, parent, false)
return ViewHolder(view, itemClick)
}
override fun getFilter(): Filter {
return searchFilter
}
private val searchFilter: Filter = object: Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList: ArrayList<Charges> = ArrayList()
if (constraint!!.isEmpty()) {
filteredList.addAll(chargesAll)
} else {
for(item in chargesAll) {
if
(item.chargeDescription.toLowerCase().contains(constraint.toString().toLowerCase())) {
filteredList.add(item)
}
}
}
val searchResults = FilterResults()
searchResults.values = filteredList
return searchResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
charges.clear()
charges.addAll(results!!.values as Collection<Charges>)
notifyDataSetChanged()
}
}
}
Activity
descriptionSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
return false
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.isNotEmpty()) {
searchViewBar(newText)
} else {
if (newText.isEmpty()) { //I ADDED THIS TO RELOAD THE ADAPTER
charges.clear()
chargeList()
}
}
return false
}
})
private fun searchViewBar(newText: String) {
chargesListener = chargesCollectionRef
.whereGreaterThanOrEqualTo(CHARGE_DESCRIPTION, newText)
.whereLessThanOrEqualTo(CHARGE_DESCRIPTION, newText+"z")
.addSnapshotListener(this) { snapshot, exception ->
if (exception != null) {
println("error")
}
if (snapshot != null) {
charges.clear()
parseData(snapshot)
}
}
}
fun parseData(snapshot: QuerySnapshot) {
for (document in snapshot.documents) {
val data = document.data
val chargeCode = data!![CHARGE_CODE] as String
val chargeDescription = data[CHARGE_DESCRIPTION] as String
val chargeSpecialty = data[CHARGE_SPECIALTY] as String
val newChargeList = Charges(chargeCode, chargeDescription, chargeSpecialty)
charges.add(newChargeList)
}
chargesFromAdapter.notifyDataSetChanged()
}

How to query different references in firebase and check if one of the child is equal to the other childs value

I am not sure if the question seems perfect but I wIll appreciate any help
here is my firebase database
I have 2 different references.one is the user and the other is the item..its a buying and selling app..I want to set the users image to his item on the show item activity...
how do i go about the query..
if boyo#yahoo.com post an item,I want the other users to see his image and his full names under the showitemactivity
here is a screen showof my show activity item
thanks in anticipation
here is the code I am using
private fun getItemDetails(itemId: String?) {
val firebaseDatabase: FirebaseDatabase = FirebaseDatabase.getInstance()
var rootRef: DatabaseReference = firebaseDatabase.reference
rootRef.child("Items").child(itemId!!).addValueEventListener(object : ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
val sellerid: String = p0.child("uid").value.toString()
(imagesArray as ArrayList<Items>).clear()
for (snapshot in p0.children) {
val items = p0.getValue(Items::class.java)
/* var decimalFormat: DecimalFormat = DecimalFormat("#,##0.00")
var formattedValue: String = decimalFormat.format(items!!.getpricee())*/
(imagesArray as ArrayList<Items>).add(items!!)
priceTV!!.text = items.getpricee()
titleTV!!.text = items.gettitlee()
descriptionTV!!.text = items.getdescriptionn()
categoryTV!!.text = items.getcategoryy()
subcategoryTV!!.text = items.getsubcategoryy()
state!!.text = items.getstatee()
lga!!.text = items.getlgaa()
date!!.text = items.getdatee()
time!!.text = items.gettimee()
rootRef.child("Users").addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
if(p0.exists()) {
for(snapshot in p0
.children) {
val users = snapshot.getValue(Users::class.java)
if(snapshot.equals(sellerid)) {
username!!.text = users!!.getFullnamee()
if(users.getImageee().isEmpty()) {
userImage!!.setImageResource(R.drawable.avatarplaceholder)
} else {
Picasso.get().load(users.getImageee())
.into(userImage)
}
}
}
}
}
})
}
}
sliderPageAdapter?.notifyDataSetChanged()
}
override fun onCancelled(p0: DatabaseError) {
}
})
}

Cannot fetch messages from another user FirebaseDatabase

This is a simple chat messaging app, using Firebase Realtime Database and the Authentication. I have successfully created the database, but I cannot fetch the message from one user to another. Because, when I send the message/click the send button, it creates different nodes, I think its a string node with quotation mark in the name. The node structure I created is user-messages --> fromId(current user uid) --> toUid(to whom the messages are sent).
To make more clear explanation, this is the screenshot of what happened.
I think the problem is, it creates a different nodes with the "uid", which means string. So the database has two nodes, one with the "...", and the other the normal one. For some info, I use some library such as Picasso image load, groupie, and parcelize.
This is the source code for the ChatLogActivity
import...
class ChatLogActivity : AppCompatActivity() {
companion object {
val TAG = "ChatLog"
}
val adapter = GroupAdapter<ViewHolder>()
var toUser: User? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat_log)
recyclerview_chat_log.adapter = adapter
toUser = intent.getParcelableExtra(NewMessageActivity.USER_KEY)
titleChatLog.text = toUser?.nama
listenForMessages()
arrowBack.setOnClickListener {
finish()
}
send_button_chat_log.setOnClickListener {
Log.d(TAG, "Attempt to send message.....")
performSendMessage()
}
}
private fun listenForMessages() {
val fromId = FirebaseAuth.getInstance().uid
val toId = toUser?.uid
val ref = FirebaseDatabase.getInstance().getReference("/user-messages/$fromId/$toId")
ref.addChildEventListener(object : ChildEventListener {
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
val chatMessage = p0.getValue(ChatMessage::class.java)
if (chatMessage != null) {
Log.d(TAG, chatMessage.text)
if (chatMessage.fromId == FirebaseAuth.getInstance().uid) {
val currentUser = LatestMessageActivity.currentUser?: return
adapter.add(ChatFromItem(chatMessage.text, currentUser))
} else {
adapter.add(ChatToItem(chatMessage.text, toUser!!))
}
}
}
override fun onCancelled(p0: DatabaseError) {
}
override fun onChildChanged(p0: DataSnapshot, p1: String?) {
}
override fun onChildMoved(p0: DataSnapshot, p1: String?) {
}
override fun onChildRemoved(p0: DataSnapshot) {
}
})
}
private fun performSendMessage() {
val text = edittext_chat_log.text.toString()
val fromId = FirebaseAuth.getInstance().uid
val user = intent.getParcelableExtra<User>(NewMessageActivity.USER_KEY)
val toId = user.uid
val ref = FirebaseDatabase.getInstance().getReference("/user-messages/$fromId/$toId").push()
val toRef = FirebaseDatabase.getInstance().getReference("/user-messages/$toId/$fromId ").push()
if (fromId == null) return
val chatMessage = ChatMessage(ref.key!!, text, fromId, toId, System.currentTimeMillis() / 1000)
ref.setValue(chatMessage)
.addOnSuccessListener {
Log.d(TAG, "Save our chat message: ${ref.key}")
edittext_chat_log.text.clear()
recyclerview_chat_log.scrollToPosition(adapter.itemCount - 1)
}
toRef.setValue(chatMessage)
}
}
class ChatFromItem(val text: String, val user: User) : Item<ViewHolder>() {
override fun getLayout(): Int {
return R.layout.chat_from_row
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val uri = user.profileImageUrl
val targetImageView = viewHolder.itemView.imageViewFrom
Picasso.get().load(uri).into(targetImageView)
viewHolder.itemView.textViewFrom.text = text
}
}
class ChatToItem(val text: String, val user: User) : Item<ViewHolder>() {
override fun getLayout(): Int {
return R.layout.chat_to_row
}
override fun bind(viewHolder: ViewHolder, position: Int) {
val uri = user.profileImageUrl
val targetImageView = viewHolder.itemView.imageViewTo
Picasso.get().load(uri).into(targetImageView)
viewHolder.itemView.textViewTo.text = text
}
}
And this is the ChatMessage, class that store the messages.
class ChatMessage(val id:String, val text: String, val fromId: String, val toId: String, val timeStamp: Long) {
constructor(): this("", "", "", "", -1)
}
I'm very glad if anyone can help this problem. Thank you

Resources