how to query list of data in firebase android - android

I have List = {1,2}, I want to retrieve data of child ("deals/{1,2})
fun deal_detail(deal: List<Deals>): Flowable<List<Deal_detail>> =
observeValueEvent(ref1.child("deals")).map { snapshot ->
snapshot.children.mapNotNull { child ->
var name = child.child("$deal/dealable/keyword").getValue().toString()
Deal_detail(name)
}
}

i solve the problem use map for list data.....
ref: https://medium.com/#justintulk/how-to-query-arrays-of-data-in-firebase-aa28a90181ba
fun deal_detail(deal: List<Deals>): Flowable<Deal_detail> =
observeValueEvent(ref1.child("deals")).map {
snapshot->
var deal1 = deal.map { id ->
snapshot.child(id.name.toString()).child("/dealable/keyword").getValue().toString()
}
var image=deal.map { id->
snapshot.child(id.name.toString()).child("dealable/image").getValue().toString()
}
Deal_detail(deal1,image)
}

Related

Jetpack Compose: Room returns null for list of items

I am trying to get list of todos from database with livedata however, while debugging it always shows null for value. I have provided my files below.
My Dao:
#Query("SELECT * FROM todo_table WHERE IIF(:isCompleted IS NULL, 1, isCompleted = :isCompleted)")
fun getTodos(isCompleted: Boolean?): LiveData<List<Todo>>
My ViewModel:
private var _allTodoList = MutableLiveData<List<Todo>>()
var allTodoList: LiveData<List<Todo>> = _allTodoList
init {
viewModelScope.launch(Dispatchers.IO) {
val list = todoRepository.getTodos(null)
_allTodoList.postValue(list.value)
}
}
fun onFilterClick(todoType: Constants.TodoType) {
when (todoType) {
Constants.TodoType.ALL -> {
viewModelScope.launch(Dispatchers.IO) {
val list = todoRepository.getTodos(null)
_allTodoList.postValue(list.value)
}
}
Constants.TodoType.COMPLETED -> {
viewModelScope.launch(Dispatchers.IO) {
val list = todoRepository.getTodos(true)
_allTodoList.postValue(list.value)
}
}
Constants.TodoType.INCOMPLETE -> {
viewModelScope.launch(Dispatchers.IO) {
val list = todoRepository.getTodos(false)
_allTodoList.postValue(list.value)
}
}
}
}
My MainActivity:
val allTodoList = viewModel.allTodoList.observeAsState()
allTodoList.value?.run {//value is always null
if (!isNullOrEmpty()) {
...
} else {
...
}
}
While debugging I found that allTodoList.value is always null however, when I manually run same query in app inspection I the get the desired results.
You can simplify your code, see if it works.
ViewModel only needs this:
val allTodoList: LiveData<List<Todo>> = todoRepository.getTodos(null)
MainActivity:
val allTodoList by viewModel.allTodoList.observeAsState()
if (!allTodoList.isNullOrEmpty()) {
...
} else {
...
}
You are not observing the LiveData you get from Room.
YourDao.getTodos() and LiveData.getValue() are not suspend functions, so you get the current value, which is null because Room has not yet fetched the values from SQLite.
A possible solution would be to set the todo type as a live data itself and use a switchMap transformation in the ViewModel :
private val todoType = MutableLiveData<Constants.TodoType>(Constants.TodoType.ALL)
val allTodoList: LiveData<List<Todo>> = androidx.lifecycle.Transformations.switchMap(todoType) { newType ->
val typeAsBoolean = when(newType) {
Constants.TodoType.ALL -> null
Constants.TodoType.COMPLETED -> true
Constants.TodoType.INCOMPLETE -> false
else -> throw IllegalArgumentException("Not a possible value")
}
// create the new wrapped LiveData
// the transformation takes care of subscribing to it
// (and unsubscribing to the old one)
todoRepository.getTodos(typeAsBoolean)
}
fun onFilterClick(todoType: Constants.TodoType) {
// triggers the transformation
todoType.setValue(todoType)
}
This is in fact the exact use case demonstrated in the reference doc

Groupie RecyclerView OnClick returns empty Item

I am using a room database with live data
Retrieving information from Database
private fun getData(query: String) {
var dataIssueJson: MutableList<DataIssue>
dataViewModel.searchDatabase(query).observe(this, {
dataList = it as MutableList<Data>
data = if (dataList.isNotEmpty())
dataList[0] else
Data()
val gson = Gson()
val dataIssueString =
if (dataList.isNotEmpty()) {
dataList[0].dataIssue
} else ""
val type = object : TypeToken<MutableList<DataIssue>>() {}.type
dataIssueJson = if (dataIssueString.isNotEmpty())
gson.fromJson(dataIssueString, type)
else
mutableListOf()
viewAdapter.clear()
initRecyclerView(dataIssueJson.toDataIssueItem())
val dataStatus = if (dataList.isNotEmpty())
dataList[0].dataStatus
else "Status"
dataViewModel.dataStatus.value = dataStatus
colorStatus(dataStatus)
})
}
Recyclerview
private fun initRecyclerView(dataItem: List<dataIssueItem>) {
binding.recyclerViewDataIssues.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
adapter = viewAdapter.apply {
addAll(botItem)
setOnItemClickListener(onClickItem)
notifyDataSetChanged()
}
scrollToPosition(binding.recyclerViewDataIssues.adapter!!.itemCount - 1)
}
}
private fun List<DataIssue>.toDataIssueItem(): List<DataIssueItem> {
return this.map { DataIssueItem(it) }
}
OnClick
private val onClickItem = OnItemClickListener { item, view ->
if (item is DatatIssueItem) {
Log.d(AppConstants.MAIN_ACTIVITY_TAG, "DataIssue:${item.dataIssue.dataIssue}, date&time: ${item.dataIssue.dateAndTimeOfIssue}")
}
}
The Recyclerview works fine, but when I click on the Recyclerview Item it returns an empty Item and I'm just not sure why
Yeah, I figured it out. The data comes from the Item itself, so make sure to let the data be accessible from the Item.

How to filter data before adding it to the recyclerview?

I am pulling some data from the firestore and after that, I want to filter it before adding it to the recyclerView. Is that possible?
The data that I have pulled is an ArrayList which has a field called 'order_status'. It may contain many different statuses, but I want to filter it out so that I will be left with only "Pending", "Order Received", "In Process", "Packed".
The following code is used to pull the data from the firestore
fun getOrderStatusList(fragment: OrdersByStatusFragment) {
mFireStore.collection("orders")
.whereIn(
"address.pinCode",
listOf("676767", "652365","679577")
)
.get()
.addOnSuccessListener { document ->
val list: ArrayList<OrderStatus> = ArrayList()
for (i in document.documents) {
val orderStatus = i.toObject(OrderStatus::class.java)!!
orderStatus.id = i.id
list.add(orderStatus)
}
fragment.successOrderStatusList(list)
}
.addOnFailureListener {
fragment.hideProgressDialog()
}
}
The following code is part of the fragment.
fun successOrderStatusList(orderStatusList: ArrayList<OrderStatus>) {
hideProgressDialog()
if (orderStatusList.size > 0) {
rv_order_by_status.visibility = View.VISIBLE
tv_no_orders_by_status_found.visibility = View.GONE
rv_order_by_status.layoutManager = LinearLayoutManager(activity)
rv_order_by_status.setHasFixedSize(true)
val orderStatusListAdapter =
OrderStatusListAdapter(requireActivity(), orderStatusList,this#OrdersByStatusFragment)
rv_order_by_status.adapter = orderStatusListAdapter
} else {
rv_order_by_status.visibility = View.GONE
tv_no_orders_by_status_found.visibility = View.VISIBLE
}
}
In your case, you should just check for status inside the loop.
F.e:
if(document.orderStatus == "Pending")
//addTolist

How can I solve a scoping issue where variable is not maintained outside of kotlin `forEach` loop

I am having some scoping issues that I don't know how to solve. In the code below the array allRecentItems is not populated after it has been assigned Items inside the forEach loop.
The idea is to query the Room database for the ID of an Item and then use a function getItemById() to return the details for the item with that ID by querying a Firestore collection for a document with that ID.
What method can I use to solve this problem? Thanks.
Approach 1
override fun getAllRecentlyTappedItems(callback: (ArrayList<Item>) -> Unit): LiveData<ArrayList<Item>>
{
val allRecentItems: ArrayList<Item> = arrayListOf()
launch {
val recentlyTappedItems: List<EntityRecentItems> = withContext(Dispatchers.IO){
itemDatabase.entityRecentlyTappedItem().getAll()
}
recentlyTappedItems.forEach { entityRecentItem ->
getItemById(entityRecentItem.itemId){ item: Item ->
allRecentItems.add(item)
//`item` is present here
Log.d(
this.javaClass.simpleName,
"getAllRecentlyTappedItems: {add: ${item.name}}"
)
}
}
// `allRecentItems` is empty at this point where I need it.
Log.d(
this.javaClass.simpleName,
"getAllRecentlyTappedItems: {final allRecentItems: ${allRecentItems}}"
)
mutableLiveDataItemArrayList.postValue(allRecentItems)
}
// mutableLiveDataItemArrayList not updated yet.
return mutableLiveDataItemArrayList
}
getItemById function
override fun getItemById(itemId: String, callback: (item: Item) -> Unit)
{
firestore.collection(Constants.FirebasePaths.DATABASE_ITEMS)
.document(itemId)
.get()
.addOnSuccessListener { documentSnapshot ->
if(documentSnapshot != null)
{
Log.d(this.javaClass.simpleName,
"getItemById: {" +
"itemName: ${documentSnapshot.data!![Constants.FirebaseDocumentSnapshotKeys.DATABASE_ITEMS_ITEM_NAME].toString()}" +
"}")
callback(
Item(name = documentSnapshot.data!![Constants.FirebaseDocumentSnapshotKeys.DATABASE_ITEMS_ITEM_NAME].toString())
)
}
}
}
Approach 2
override fun getAllRecentlyTappedItems(callback: (ArrayList<Item>) -> Unit): LiveData<ArrayList<Item>>
{
launch {
val listOfRecentItems = async(Dispatchers.IO){
itemDatabase.entityRecentlyTappedItem().getAll()
}
val res = async(Dispatchers.Main) {
val allRecentItems: ArrayList<Item> = arrayListOf()
listOfRecentItems.await().forEach { recentItem ->
Log.d(this.javaClass.simpleName, "getAllRecentlyTappedItems: " +
"{recentTappedIdFromDatabase: ${recentItem.itemId}}")
getItemById(recentItem.itemId) {item: Item ->
Log.d(this.javaClass.simpleName, "getAllRecentlyTappedItems: " +
"{(itemId: ${item.itemId}, itemName: ${item.itemName})}")
allRecentItems.add(item)
}
}
// `res` is always empty at this point
allRecentItems
}
// `res.await()` returns an empty array
Log.d(this.javaClass.simpleName, "getAllRecentlyTappedItems: " +
"{allRecentItems: ${res.await()}}")
mutableLiveDataItemArrayList.postValue(res.await())
}
return mutableLiveDataItemArrayList
}
You will want to look into Coroutine.async. Problem is that the method is returning before the launch can execute and complete. You require some asynchronous functionality.
val deferred: Deferred<ArrayList<Item>> = GlobalScope.async(Dispatchers.IO){
val recentlyTappedItems: List<EntityRecentItems> = withContext(Dispatchers.IO){
itemDatabase.entityRecentlyTappedItem().getAll()
}
val recentItems: List<Item> = arrayListOf()
recentlyTappedItems.forEach { entityRecentItem ->
getItemById(entityRecentItem.itemId){ item: Item ->
recentItems.add(item)
}
}
recentItems
}
val allRecentItems = deferred.await()
This would need to be wrapped in a suspend fun I believe.
suspend fun getAllRecentItems(): List<Item> {
// Above snippet
}
Your code looks incomplete with regards to mutableLiveDataItemArrayList and the methods return type, but I am sure this will help get you to where you need.

How to copy a property between 2 lists of different types using declarative Kotlin?

Context
Using a declarative approach in Kotlin, need to copy a single name property from List of User objects to a List of UserDetail objects based on matching id properties as shown below:
val users = Arrays.asList(
User(1, "a"),
User(2, "b")
)
val details = Arrays.asList(
UserDetail(1),
UserDetail(2)
)
val detailsWithName = copyNameToUser(users, details)
Models are:
class User {
var id = -1;
var name = "" // given for all Users
constructor(id: Int, name: String)
// ...
}
class UserDetail {
var id = -1;
var name = "" // blank for all UserDetails
constructor(id: Int)
// ...
}
Problem
Tried to use a declarative approach via forEach iterable function:
fun copyNameToDetails(users: List<User>, details: List<UserDetail>): List<UserDetail> {
details.forEach(d ->
users.forEach(u ->
if (d.id == u.id) {
d.name = u.name
}
)
)
return details
}
This can be achieved in Java as shown below:
private static List<UserDetail> copyNameToDetails(List<User> users, List<UserDetail> details) {
for (UserDetail d: details) {
for (User u : users) {
if (d.id == u.id) {
d.name = u.name;
}
}
}
return details;
}
Question
How can this be done in Kotlin using a declarative approach?
You make too many iterations over both lists (users.size * details.size) so creating a hashmap can fix it a bit:
fun copyNameToUsers(users: List<User>, details: List<UserDetail>): List<UserDetail> {
val usersById = users.associate { it.id to it }
details.forEach { d ->
usersById[d.id]?.let { d.name = it.name }
}
return details
}
An other approach with non mutable values :
data class User(val id: Int = -1, val name: String = "")
data class UserDetail(val id: Int = -1, val name: String = "")
private fun List<UserDetail>.copyNameToUser(users: List<User>): List<UserDetail> = map { userDetail ->
users.firstOrNull { userDetail.id == it.id }?.let { userDetail.copy(name = it.name) } ?: userDetail
}

Categories

Resources