I have a RecyclerView that utilizes the FireaseUI and orders all objects from the "Polls" node by the "timestamp" field (sequentially).
New Fragment - .onViewCreated()
Query queryStore = FirebaseFirestore.getInstance()
.collection(POLLS_LABEL)
.orderBy("timestamp", Query.Direction.ASCENDING);
//Cloud Firestore does not have any ordering; must implement a timestampe to order sequentially
FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
.setQuery(queryStore, Poll.class)
.build();
mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
#Override
protected void onBindViewHolder(#NonNull final PollHolder holder, final int position, #NonNull Poll model) {
holder.mPollQuestion.setText(model.getQuestion());
String voteCount = String.valueOf(model.getVote_count());
//TODO: Investigate formatting of vote count for thousands
holder.mVoteCount.setText(voteCount);
Picasso.get()
.load(model.getImage_URL())
.fit()
.into(holder.mPollImage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
String recyclerPosition = getSnapshots().getSnapshot(position).getId();
Log.v("Firestore ID", recyclerPosition);
toClickedPoll.putExtra("POLL_ID", recyclerPosition);
startActivity(toClickedPoll);
}
});
}
I have another layout in my app that subscribes to this same node, but instead queries by "followers" and then by "timestamp.:
Following Fragment - .onViewCreated()
Query queryStore = FirebaseFirestore.getInstance()
.collection(POLLS_LABEL)
.whereArrayContains("followers", mUserId)
.orderBy("timestamp", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
.setQuery(queryStore, Poll.class)
.build();
mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
#Override
protected void onBindViewHolder(#NonNull final PollHolder holder, final int position, #NonNull Poll model) {
holder.mPollQuestion.setText(model.getQuestion());
String voteCount = String.valueOf(model.getVote_count());
//TODO: Investigate formatting of vote count for thousands
holder.mVoteCount.setText(voteCount);
Picasso.get()
.load(model.getImage_URL())
.fit()
.into(holder.mPollImage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
String recyclerPosition = getSnapshots().getSnapshot(position).getId();
Log.v("Firestore ID", recyclerPosition);
toClickedPoll.putExtra("POLL_ID", recyclerPosition);
startActivity(toClickedPoll);
}
});
}
In the first scenario, UI items populate, in real-time, into my RecyclerView as they are added to Firebase. However, when I query by ".whereArrayContains," I do not get this same behavior, and I was curious as to why. The items only reappear when I restart the application:
Edit:
I commented out the below line:
// .whereArrayContains("followers", mUserId)
and the behavior performed as expected, therefore I can isolate the issue to the .whereArrayContains() query. It is the only difference between each Fragment.
This is happening because when you are using whereArrayContains() and orderBy() methods in the same query, an index is required. To use one, go to your Firebase Console and create it manually or if you are using Android Studio, you'll find in your logcat a message that sounds like this:
W/Firestore: (0.6.6-dev) [Firestore]: Listen for Query(products where array array_contains YourItem order by timestamp) failed: Status{code=FAILED_PRECONDITION, description=The query requires an index. You can create it here: ...
You can simply click on that link or copy and paste the url into a web broswer and you index will be created automatically.
Why is this index needed?
As you probably noticed, queries in Cloud Firestore are very fast and this is because Firestore automatically creates an indexes for any fields you have in your document. When you need to order your items, a particular index is required that should be created as explained above. However, if you intend to create the index manually, please also select from the dropdown the corresponding Array contains option, as in the below image:
Related
I'm struggling with Android Auto using Desktop Head Unit Emulator.
So far I create my media app in Android Auto, start it and all media albums where shown.
However, when I click on an entry, nothing happens.
It is not clear to me how to catch the click in order to further start an action.
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
int maximumRootChildLimit = rootHints.getInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
/* defaultValue= */ 4);
Bundle extras = new Bundle();
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
extras.putBoolean(
MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
return new BrowserRoot(MY_MEDIA_ROOT_ID, extras);
}
#Override
public void onLoadChildren(#NonNull String parentId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
if (MY_MEDIA_ROOT_ID.equals(parentId)) {
for (Album album : allAlbums) {
.....
MediaDescriptionCompat desc =
new MediaDescriptionCompat.Builder()
.setMediaId(mediaID)
.setTitle(title)
.setSubtitle(artist)
.setIconBitmap(bitmap(mediaID))
.setExtras(extras)
.build();
mediaItems.add(albumList);
}
}
result.sendResult(mediaItems);
}
I set the mediaID in my PlaybackStateCompat.Builder:
Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID,"mediaID");
playbackstateBuilder.setExtras(playbackStateExtras);
I set the mediaID in my MediaMetadataCompat.Builder
metadataBuilder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID,"mediaID");
mMediaSessionCompat.setMetadata(metadataBuilder.build());
What have I overlooked in the documentation or didn't understand.
Thanks
Alejandro
note the following procedure, please:
FLAG_Browsable: use this flag, then the MediaID is returned in getLoadChildren.
FLAG_PLAYABLE: use this flag, then the MediaID is returned to onPlayFromMediaID. In onPlayFromMediaIDyou have to put the logic how your mediaPlayerknows what to do with the handed over mediaID
I noticed that you didn't define any tabs. In onLoadChildren several queries on the mediaID make sense. The first level defines the tabs - these are missing in the post or in your code.
e.g:
MediaDescriptionCompat desc =
new MediaDescriptionCompat.Builder()
.setMediaId(AUDIOBOOK_HEADER)
.setTitle("Library")
.build();
MediaBrowserCompat.MediaItem albumList =
new MediaBrowserCompat.MediaItem(desc,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
mediaItems.add(albumList);
desc =
new MediaDescriptionCompat.Builder()
.setMediaId(LATEST_HEADER)
.setTitle("Recently played")
.build();
itemList =
new MediaBrowserCompat.MediaItem(desc,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
mediaItems.add(itemList);
I'm facing with a not so cool problem and been thinking a way through it.
As in the above picture I'm using the timestamp field which has a timestamp datatype, to do a orderBy descending query.
After the query is done I need to enter the Order Item subcollection to get all the data.
CollectionReference orderRef = fStore.collection(Reference.ORDER_COL);
orderRef.orderBy(Reference.TIME_STAMP, Query.Direction.DESCENDING)
.whereEqualTo(Reference.CUSTOMER_UID, logInSession.getUID())
.get()
.addOnSuccessListener(this, new OnSuccessListener<QuerySnapshot>() {
#SuppressWarnings("ConstantConditions")
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
if (!queryDocumentSnapshots.isEmpty()) {
for (QueryDocumentSnapshot docSnap : queryDocumentSnapshots) {
Log.e("TAG 1", "Loop 1");
final boolean packing = docSnap.getBoolean(Reference.STATUS_PACKING);
final boolean outForDelivery = docSnap.getBoolean(Reference.STATUS_OUT_FOR_DELIVERY);
final boolean delivered = docSnap.getBoolean(Reference.STATUS_DELIVERED);
final String orderDate = docSnap.getString(Reference.DATE);
final int totalAmount = docSnap.getLong(Reference.TOTAL_AMOUNT).intValue();
CollectionReference orderQuery = orderRef.document(docSnap.getId()).collection(Reference.ORDER_ITEM_COL);
orderQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value, #Nullable FirebaseFirestoreException error) {
if (!value.isEmpty()) {
for (QueryDocumentSnapshot docSnap : value) {
Log.d("TAG 2" , "Loop 2");
String productName = docSnap.getString(Reference.PRODUCT);
String totalPrice = String.valueOf(docSnap.getLong(Reference.PRICE).intValue());
String quantity = String.valueOf(docSnap.getLong(Reference.QUANTITY).intValue());
String thumbnail = docSnap.getString(Reference.THUMBNAIL);
orderHistoryList.add(new OrderHistory(productName, thumbnail, quantity, totalPrice, orderDate, packing, outForDelivery, delivered));
adapter.notifyDataSetChanged();
}
}
}
});
}
} else {
noOrderTxt.startAnimation(AnimationUtils.loadAnimation(OrderActivity.this, R.anim.fade_in));
noOrderTxt.setVisibility(View.VISIBLE);
}
}
});
Above are the coding I did and the output I get is all messed up and is not queried on timestamp, there is also a whereEqualTo() method which is working fine.
As you can see I use whereEqualTo() and orderBy() and also have created the required Composite Indexes.
I place a log on both the for loops, here the first for loop executed first and then the second for loop came in which is confusing. I thought after the first for loop the second for loop will be executed but it didn't happen, not like the regular nested for loop it didn't work as I plan so I think that's the reason behind the messed up result I get on my app.
What I want is a proper DESCENDING orderBy query which I will be displaying to a RecyclerView.
Please help me out guys and thank you in advance. If you need anymore codes or details please tell me :)
I am trying to use the FirestorePagingAdapter to display a list of all users in my firestore database. I am using FirestorePagingAdapter instead of FirestoreRecyclerAdapter to minimize the number of reads as FirestorePagingAdapterdoesn't read the whole list of documents while FirestoreRecyclerAdapter does. I am able to display the paginated list successfully but I need to implement onClickListener onto it and on click of every item, I need to open another activity which shows a detailed description of the particular user which was clicked. For this, I need to pass the documentId of the clicked user to the next activity.
But unfortunately, FirestorePagingAdapter doesn't have getSnapshots() method so that I use getSnapshots().getSnapshot(position).getId().
On the other hand, FirestoreRecyclerAdapter has this method which makes fetching the document id a very easy task. Something like this: How to get document id or name in Android in Firestore db for passing on to another activity?
// Query to fetch documents from user collection ordered by name
Query query = FirebaseFirestore.getInstance().collection("users")
.orderBy("name");
// Setting the pagination configuration
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.setPageSize(20)
.build();
FirestorePagingOptions<User> firestorePagingOptions = new FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(query, config, User.class)
.build();
firestorePagingAdapter =
new FirestorePagingAdapter<User, UserViewHolder>(firestorePagingOptions){
#NonNull
#Override
public UserViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_user_layout, parent, false);
return new UserViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull UserViewHolder holder, int position, #NonNull User user) {
holder.setUserName(user.name);
holder.setStatus(user.status);
holder.setThumbImage(user.thumb_image, UsersActivity.this);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent userProfileIntent = new Intent(UsersActivity.this, UserProfileActivity.class);
// Need to fetch the user_id to pass it as intent extra
// String user_id = getSnapshots().getSnapshot(position).getId();
// userProfileIntent.putExtra("user_id", user_id);
startActivity(userProfileIntent);
}
});
}
};
I was able to do this by using SnapshotParser in setQuery method. Through this, I was able to modify the object which I got from the firestore. The documentSnapshot.getId() method returns the document id.
FirestorePagingOptions<User> firestorePagingOptions = new FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(query, config, new SnapshotParser<User>() {
#NonNull
#Override
public User parseSnapshot(#NonNull DocumentSnapshot snapshot) {
User user = snapshot.toObject(User.class);
user.userId = snapshot.getId();
return user;
}
})
.build();
In the User class, I just added another field "String userId" in the User class. The userId field doesn't exist in my firestore document.
In the onClickListener, I can then directly use user.userId to get the document id and send it to other activity.
While trying to access the document snapshot from itemView, I found out this
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int pos = getAdapterPosition();
if (pos != RecyclerView.NO_POSITION && listener != null) {
String docId = getItem(pos).getId();
Toast.makeText(context, "doc Id: "+docId, Toast.LENGTH_SHORT).show();
//listener.onItemClick(getSnapshots().getSnapshot(pos), pos, docId);
listener.onItemClick(getItem(pos), pos, docId);
}
}
});
As mentioned here, the getItem() returns the item's data object.
As you already noticed:
String id = getSnapshots().getSnapshot(position).getId();
Doesn't work, it works only when using FirestoreRecyclerAdapter. So to solve this, you need to store the id of the document as property of your document. If the id of the document is the id of the user that comes from the Firebase autentication, then simply store that uid. If you are not using the uid, get the id of the document and pass it to the User constructor when creating a new object like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference usersRef = rootRef.collection("users");
String id = eventsRef.document().getId();
User user = new User(id, name, status, thumb_image);
usersRef.document(id).set(user);
I spent a day trying to get the id of the document as I am now using the FirestorePagingAdapter. For Kotlin this is what worked for me
override fun onBindViewHolder(viewHolder: LyricViewHolder, position: Int, song: Lyric) {
// Bind to ViewHolder
viewHolder.bind(song)
viewHolder.itemView.setOnClickListener { view ->
val id = getItem(position)?.id
var bundle = bundleOf("id" to id)
view.findNavController().navigate(R.id.songDetailFragment, bundle)
}
}
Hope this helps someone else in the near future. Can post full code and fully explain if anyone is confused. Happy coding!
Try using this
getSnapshots().getSnapshot(position).getId()
I have a collection of users and I want to query all users from the database and display them in a RecyclerView except one, mine. This is my db schema:
users [collection]
- uid [document]
- uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
- username: "John"
- age: 22
- //other users
How to query the database like so:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Query q = db.collection("users").whereNotEqualTo("uid", uid);
So I need this query object to be passed to a FirestoreRecyclerOptions object in order to display all the other users in RecyclerView.
Is this even possible? If not, how can I solve this? Thanks!
Edit:
options = new FirestoreRecyclerOptions.Builder<UserModel>()
.setQuery(query, new SnapshotParser<UserModel>() {
#NonNull
#Override
public UserModel parseSnapshot(#NonNull DocumentSnapshot snapshot) {
UserModel userModel = documentSnapshot.toObject(UserModel.class);
if (!userModel.getUid().equals(uid)) {
return userModel;
} else {
return new UserModel();
}
}
}).build();
After days and days of struggling with this issue, I finally found the answer. I could not solve this without the help of #Raj. Thank you so much #Raj for the patience and guidance.
First off all, according to the answer provided by #Frank van Puffelen in his answer from this post, I stopped searching for a solution that can help me pass two queries to a single adapter.
In this question, all that I wanted to achieve was to query the database to get all the users except one, me. So because we cannot combine two queries into a single instance, I found that we can combine the result of both queries. So I have created two queries:
FirebaseFirestore db = FirebaseFirestore.getInstance();
Query firstQuery = db.collection("users").whereLessThan("uid", uid);
Query secondQuery = db.collection("users").whereGreaterThan("uid", uid);
I'm having a UserModel (POJO) class for my user object. I found not one, but two ways to solve the problem. The first one would be to query the database to get all user objects that correspond to the first criteria and add them to a list. After that, query the database again and get the other user objects that correspond to the second criteria and add them to the same list. Now I have a list that contains all the users that I need but one, the one with that particular id from the queries. This is the code for future visitors:
firstQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<UserModel> list = new ArrayList<>();
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
UserModel userModel = document.toObject(UserModel.class);
list.add(userModel);
}
secondQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
UserModel userModel = document.toObject(UserModel.class);
list.add(userModel);
}
//Use the list of users
}
}
});
}
}
});
The second approach would be much shorter because I use Tasks.whenAllSuccess() like this:
Task firstTask = firstQuery.get();
Task secondTask = secondQuery.get();
Task combinedTask = Tasks.whenAllSuccess(firstTask, secondTask).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
#Override
public void onSuccess(List<Object> list) {
//This is the list that I wanted
}
});
According to the official firestore documentation:-
Cloud Firestore does not support the following type of query:
Queries with a != clause. In this case, you should split the query
into a greater-than query and a less-than query. For example, although
the query clause where("age", "!=", "30") is not supported, you can
get the same result set by combining two queries, one with the clause
where("age", "<", "30") and one with the clause where("age", ">", 30).
If you are using FirestoreRecyclerAdapter then FirestoreRecyclerOptions will directly accepts the query using setQuery() method and hence not allows you to perform client side filtering.
If you try to apply filters in onBindViewHolder() while setting the data that might results in empty items in the recycler view. In order to resolve that refer Method 2.
So, the possible solution to your problem would be to create an integer field in your users collection under every document. Eg:-
users [collection]
- uid [document]
- uid: "fR5bih7SysccRu2Gu9990TeSSyg2"
- username: "John"
- age: 22
- check: 100
In this I have created a 'check' variable whose value is 100. So, put value of 'check' in all other documents as less than 100.
Now, you can easily make a query that finds documents with check<100 as:-
Query q = db.collection("users").whereLessThan("check", 100);
This will retrieve all your documents except the one you don't want. And while setting the data you can set other parameters skipping the check variable.
Method 2 (Client Side Filtering)
We can apply a check in onBindViewHolder() method that if the retrieved uid matches with current user uid then set the height of Recycler view as 0dp. As:-
ViewUserAdapter.java
public class ViewUserAdapter extends FirestoreRecyclerAdapter<User, ViewUserAdapter.ViewUserHolder>
{
String uid;
FirebaseAuth auth;
public ViewUserAdapter(#NonNull FirestoreRecyclerOptions<User> options)
{
super(options);
auth = FirebaseAuth.getInstance();
uid = auth.getCurrentUser().getUid();
}
#Override
protected void onBindViewHolder(#NonNull ViewUserHolder holder, int position, #NonNull User model)
{
DocumentSnapshot snapshot = getSnapshots().getSnapshot(position);
String id = snapshot.getId();
if(uid.equals(id))
{
RecyclerView.LayoutParams param = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
param.height = 0;
param.width = LinearLayout.LayoutParams.MATCH_PARENT;
holder.itemView.setVisibility(View.VISIBLE);
}
else
{
holder.tvName.setText(model.name);
holder.tvEmail.setText(model.email);
holder.tvAge.setText(String.valueOf(model.age));
}
}
}
2021 Update: This Is Supported
Howdy devs. It looks like this is now supported with the where operator used like this: citiesRef.where("capital", "!=", false);
Firestore doesn't support not equal to operation. So you need to filter the data at the client side. Since in you case you only have one extra item you can filter it out.
For that you may need to build your own recycler implementation where when adding data to recycler adapter data layer, you restrict the data when ever it matches your != condition.
I haven't explored recycler implementation firebase provided so I cannot say it supports data manipulation to adapter data or not.
Here is a good resource to start implementing recycler view : https://www.androidhive.info/2016/01/android-working-with-recycler-view/
The simplest solution would be to use a PagedListAdapter and create a custom DataSource for the Firestore queries. In the DataSource the Query can be transformed into an Array or ArrayList in which you can easily remove your item before adding the data to the method callback.onResult(...).
I used a similar solution to process data after a Firestore query in order to filter and sort by a time attribute, and then re-sort by a quality score attribute in the client before passing the data back in to callback.onResult(...).
Documentation
Google: Build your own data sources
Codepath: Paging Library Guide
Data Source Sample
class ContentFeedDataSource() : ItemKeyedDataSource<Date, Content>() {
override fun loadBefore(params: LoadParams<Date>, callback: LoadCallback<Content>) {}
override fun getKey(item: Content) = item.timestamp
override fun loadInitial(params: LoadInitialParams<Date>, callback: LoadInitialCallback<Content>) {
FirestoreCollections.contentCollection
.collection(FirestoreCollections.ALL_COLLECTION)
.orderBy(Constants.TIMESTAMP, Query.Direction.DESCENDING)
.whereGreaterThanOrEqualTo(Constants.TIMESTAMP, DateAndTime.getTimeframe(WEEK))
.limit(params.requestedLoadSize.toLong())
.get().addOnCompleteListener {
val items = arrayListOf<Content?>()
for (document in it.result.documents) {
val content = document.toObject(Content::class.java)
items.add(content)
}
callback.onResult(items.sortedByDescending { it?.qualityScore })
}
}
override fun loadAfter(params: LoadParams<Date>, callback: LoadCallback<Content>) {
FirestoreCollections.contentCollection
.collection(FirestoreCollections.ALL_COLLECTION)
.orderBy(Constants.TIMESTAMP, Query.Direction.DESCENDING)
.startAt(params.key)
.whereGreaterThanOrEqualTo(Constants.TIMESTAMP, DateAndTime.getTimeframe(WEEK))
.limit(params.requestedLoadSize.toLong())
.get().addOnCompleteListener {
val items = arrayListOf<Content?>()
for (document in it.result.documents) {
val content = document.toObject(Content::class.java)
items.add(content)
}
val sortedByQualityScore = ArrayList(items.sortedByDescending { it?.qualityScore })
callback.onResult(sortedByQualityScore)
sortedByQualityScore.clear()
}
}
}
Simpler and earlier client-side filtering (when you add items to your list):
Get the current user's ID by using Firestore's standard method.
Get the name of the doc for all the users in your user collection.
Before adding the user to
your RecyclerView list, check that the user it is about to add to your list is not the current user.
When done is this way, you can use the "not equals" method on the client side and not get into any Firestore issues. Another benefit is that you don't have to mess with your adapter or hide the view from a list-item you didn't want in the recycler.
public void getUsers(final ArrayList<Users> usersArrayList, final Adapter adapter) {
CollectionReference usersCollectionRef = db.collection("users");
Query query = usersCollectionRef
.whereEqualTo("is_onboarded", true);
query.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
final String otherUserID = document.getId();
FirebaseUser user = mAuth.getCurrentUser();
String currentUserID = user.getUid();
if (!otherUserID.equals(currentUserId)) {
usersArrayList.add(new User(otherUserID));
adapter.notifyDataSetChanged(); //Ensures users are visible immediately
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
You don't have to do all this
Just do normal query and hide the layout by setting getLayoutParams().height and width to 0 respectively. See example below.
if(firebaseUserId.equalIgnoreCase("your_holder_firebase_user_id"){
holder.mainLayout.setVisibility(View.INVISIBLE);
holder.mainLayout.getLayoutParams().height = 0;
holder.mainLayout.getLayoutParams().width = 0;
}else {
//show your list as normal
}
//This will hide any document snapshot u don't need, it will be there but hidden
here's my solution with flutter for usernames
Future<bool> checkIfUsernameExistsExcludingCurrentUid(
// TODO NOT DONE
String username,
String uid) async {
print("searching db for: $username EXCLUDING SELF");
bool exists = true;
QuerySnapshot result = await _firestore
.collection(USERS_COLLECTION)
.where(
"username",
isEqualTo: username,
)
.getDocuments();
List _documents = result.documents;
_documents.forEach((element) {
if (element['uid'] == uid) {
exists = false;
} else {
return true;
}
});
return exists;
}
I have a chatting application which is made using firebase. It works fine. But some users send slangs or stuffs like that. I use Firebase Recyclerview. My connection is direct to database. So I can't verify if the message is good or bad before data gets added.
What I want to do is, I want to send that data to some other location. Then after verification using cloud functions, I want to add it to original chat room database.
Till that step I have no problems. But when user sends a message. I dont want the user to wait until data gets updates. Cloud Functions takes 2-3 seconds for me. I want to add that data locally with a sending status and then update it when it gets updated on the chat room database.
So What's the best way to do that..
There is nothing wrong with my code. It's just basic firebase adapter..
Query query = FirebaseDatabase.getInstance()
.getReference()
.child("rooms").child("Off-Topic").child("chat")
.limitToLast(500);
firebaseOptions = new FirebaseRecyclerOptions.Builder<ChatData>()
.setQuery(query, ChatData.class)
.build();
chatAdapter = new FirebaseRecyclerAdapter<ChatData, ChatHolder>(firebaseOptions) {
#Override
protected void onBindViewHolder(ChatHolder holder, int position, ChatData model) {
}
#Override
public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.sender_text_message, parent, false);
return new ChatHolder(v);
}
};
The simplest way for you to solve this would be to add a boolean variable to your ChatData class. This variable (let's call it isGood) would be false when the message is sent and would become true when it has been updated by your Cloud Function.
So your Adapter's onBindViewHolder would be:
#Override
protected void onBindViewHolder(ChatHolder holder, int position, ChatData model) {
if(model.isGood())
holder.textView.setText(model.getMessage()); //edit this line as needed
else
holder.textView.setText("sending..."); //replace textView with the name of your actual TextView
}