I have an App which downloads data from Firebase and displays it in a RecyclerView and this works fine. What I want to do is show or hide an input element in the XML when certain conditions apply. The condition of 'yes' or 'no' is downloaded from Firebase.
It sort of works but only hides the TextView in the first item of the RecyclerView listing. How do I get it to apply to all the listed items? I will add the code and a screenshot.
Code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menus);
setUpRecyclerView();
}
private void setUpRecyclerView() {
// get menu type from MenuSelectListActivity
selectedMenu = getIntent().getStringExtra("myMenuSelected");
//get Firestore db and use selected menu for listing
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference notebookRef = db.collection(selectedMenu);
FirestoreRecyclerOptions<NoteAdapter> options = new FirestoreRecyclerOptions.Builder<NoteAdapter>()
.setQuery(query, NoteAdapter.class)
.build();
adapter = new Note(options);
final RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
DocumentReference docRef = db.collection(“delivery status”).document(“****************”);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
String myDeliveryStatus = document.getString("deliverystatus");
// if delivery status yes then allow the order to be made
if (myDeliveryStatus.equals("yes")) {
// show quantity input TextView
TextView text_quantity = (TextView) findViewById(R.id.text_view_quantity);
//Toggle
if (text_quantity.getVisibility() == View.INVISIBLE)
text_quantity.setVisibility(View.VISIBLE);
else
text_quantity.setVisibility(View.VISIBLE);
adapter.setOnItemClickListener(new Note.OnItemClickListener() {
#Override
public void onItemClick(DocumentSnapshot documentSnapshot, int position) {
String myTitle = ((TextView) recyclerView.findViewHolderForAdapterPosition(position).itemView.findViewById(R.id.text_view_title)).getText().toString();
String myPrice = ((TextView) recyclerView.findViewHolderForAdapterPosition(position).itemView.findViewById(R.id.text_view_price)).getText().toString();
String myNumberOrdered = ((TextView) recyclerView.findViewHolderForAdapterPosition(position).itemView.findViewById(R.id.text_view_quantity)).getText().toString();
***** do various calculations on the data downloaded from Firebase. Not relevant to this question so not included
}
// if no do nothing
else if (myDeliveryStatus.equals("no")) {
TextView text_quantity = (TextView) findViewById(R.id.text_view_quantity);
//Toggle to hide TextView
if (text_quantity.getVisibility() == View.VISIBLE)
text_quantity.setVisibility(View.INVISIBLE);
else
text_quantity.setVisibility(View.INVISIBLE);
}
} else {
// Log.d(TAG, "No such document");
}
} else {
// Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
Code for Adapter:
public class NoteAdapter {
private String title;
private String description;
private double price;
private int priority;
private int quantity;
private String status;
public NoteAdapter() {
//empty constructor needed
}
public NoteAdapter(String title, String description, double price, int priority, int quantity) {
this.title = title;
this.description = description;
this.price = price;
this.priority = priority;
this.quantity = quantity;
this.status = status;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public double getPrice() {
return price;
}
public int getPriority() {
return priority;
}
public int getQuantity() {
return quantity;
}
public String getStatus() {
return status;
}
}
Firstly you're doing some kind of mistake in Initializing the Recyclerview and Adapter. Which you are using as the adapter that's a just model or class.
Please have to look at this answer and change your code structure according to this Then You should do this step in BindViewHolder
adapter = new FirestoreRecyclerAdapter<NoteAdapter, ProductViewHolder>(options) {
#Override
protected void onBindViewHolder(#NonNull holder productViewHolder, int position, #NonNull NoteAdapter productModel) {
//here you can check the Yes or No like this
if (poductModel.getStatus.equalsIgnoreCase("no")){
if (text_quantity.getVisibility() == View.VISIBLE)
text_quantity.setVisibility(View.INVISIBLE);
else
text_quantity.setVisibility(View.INVISIBLE);
}
}
#NonNull
#Override
public ProductViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_product, parent, false);
return new ProductViewHolder(view);
}
};
Related
I have an app that retrieves data from Firebase specifically from Firestore, the thing is the recycler view that shows the data is empty, even though it's working in another fragment with the same logic, I don't know where i am wrong or mistaken! And as i said the fragment is totally empty, besides if i added a TextView or Button it shows normally but the recycler view doesn't show up.
ps: when i copied the same code i changed the layout name and the recycler name to point to the other layout and recycler view of the fragment;
Here is the Fragment
FavoriteFragment.java: the fragment that handles the code.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_favorite, container, false);
recyclerView = view.findViewById(R.id.fav_recycler);
linearLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(linearLayoutManager);
mAuth = FirebaseAuth.getInstance();
FirebaseUser auth_user = mAuth.getCurrentUser();
db = FirebaseFirestore.getInstance();
//Fetch Users Info
Query query = db.collection("posts")
.orderBy("posttime", Query.Direction.DESCENDING); // order the query by date
FirestoreRecyclerOptions<Posts> response = new FirestoreRecyclerOptions.Builder<Posts>()
.setQuery(query, Posts.class)
.build();
adapter = new MainAdapter(response);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);
return view;
}
fragment_favorite.xml: the layout of the fragment.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".FavoriteFragment">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="15dp">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/fav_recycler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</FrameLayout>
and
MainAdapter.java: The adapter of the Recycler view, also handles the code that makes an item (in this case a post) to be added or removed from the firestore
public class MainAdapter extends FirestoreRecyclerAdapter<Posts, MainAdapter.ViewHolder> {
/**
* Create a new RecyclerView adapter that listens to a Firestore Query. See {#link
* FirestoreRecyclerOptions} for configuration options.
*
* #param options
*/
public MainAdapter(#NonNull FirestoreRecyclerOptions options) {
super(options);
}
private FirebaseFirestore db;
private DocumentReference documentReference;
private FirebaseAuth mAuth;
boolean isthere = false;
boolean isExist = false;
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull Posts post) {
db = FirebaseFirestore.getInstance();
mAuth = FirebaseAuth.getInstance();
documentReference = db.collection("users").document(mAuth.getUid());
holder.txtTitle.setText(post.getName());
holder.txtDesc.setText(post.getTitle() + "\n" +post.getDesc() + "\n" + post.getBloodtype() + "\n" + post.getCity() + "\n" + post.getNumber()+ "\n" + post.getDeadline());
Glide.with(holder.image.getContext()).load(post.getImage())
.into(holder.image);
holder.root.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Snackbar.make(v, post.getUserid() + "\n" + post.getName() + "\n" +post.getBloodtype() + "\n" + post.getCity(), Snackbar.LENGTH_LONG)
.setAnchorView(R.id.navigation) // Set SnackBar above the BottomNavigationView
.show();
}
});
Posts newPost = new Posts(post.getUserid(), post.getName(), post.getTitle(),post.getDesc(), post.getDeadline(), post.getNumber(),
post.getCity(), post.getBloodtype(), post.getImage(), post.getPosttime(), post.getPostid());
holder.fav.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
// Toast.makeText(buttonView.getContext(), "Added to favorite", Toast.LENGTH_SHORT).show();
if (isExist == false) {
db.collection("users").document(mAuth.getUid()).collection("favorites").document(post.getPostid())
.set(newPost)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Toast.makeText(buttonView.getContext(), "Succeed \nBoolean: " + isExist, Toast.LENGTH_SHORT).show();
}
});
}
}
else {
// removefromfav(post.getPostid(), buttonView);
documentReference.collection("favorites").document(post.getPostid())
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
public void onSuccess(Void aVoid) {
// Toast.makeText(buttonView.getContext(), "Removed from favorite " + isExist, Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
}
});
documentReference.collection("favorites").document(post.getPostid())
.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if(task.isSuccessful()){
isExist = task.getResult().exists();
if (isExist == true) {
holder.fav.setChecked(true);
}
}
}
});
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.users_item, parent, false);
return new ViewHolder(view);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public LinearLayout root;
public TextView txtTitle;
public TextView txtDesc;
public ImageView image;
public ToggleButton fav;
public ViewHolder(View itemView) {
super(itemView);
root = itemView.findViewById(R.id.list_root);
txtTitle = (TextView) itemView.findViewById(R.id.list_title);
txtDesc = (TextView) itemView.findViewById(R.id.list_desc);
image = (ImageView)itemView.findViewById(R.id.list_image);
fav = (ToggleButton)itemView.findViewById(R.id.favbutton);
}
}
}
EDIT:
I added the Posts class
Posts.java:
public class Posts {
private String userid;
private String name;
private String title;
private String desc;
private String deadline;
private String number;
private String city;
private String bloodtype;
private String image;
private String posttime;
private String postid;
public Posts(){
}
public Posts(String userid,String name, String title, String desc, String deadline,
String number, String city, String bloodtype, String image, String posttime, String postid){
this.userid = userid;
this.name = name;
this.title = title;
this.desc = desc;
this.deadline = deadline;
this.number = number;
this.city = city;
this.bloodtype = bloodtype;
this.image = image;
this.posttime = posttime;
this.postid = postid;
}
public String getUserid(){
return userid;
}
public String getName(){
return name;
}
public String getTitle(){
return title;
}
public String getDesc(){
return desc;
}
public String getDeadline(){
return deadline;
}
public String getNumber(){
return number;
}
public String getCity(){
return city;
}
public String getBloodtype(){
return bloodtype;
}
public String getImage(){
return image;
}
public String getPosttime(){
return posttime;
}
public String getPostid(){
return postid;
}
}
And a screenshot of posts in firestore bellow :
Well, i figure it out, i missed the onStart and onStop to start and stop listening for the adapter.
#Override
public void onStart() {
super.onStart();
adapter.startListening();
}
#Override
public void onStop() {
super.onStop();
adapter.stopListening();
}
I used Firebase Firestore to create a realtime recycler view with pagination, but the problem is that I am trying to order the documents using Timestamp. When I try to add a new document the app crashes. This only happens when adding the first document in a collection, after that it will work fine.
This is the error I'm getting:
java.lang.IllegalArgumentException: Invalid query. You are trying to start or end a query using a document for which the field 'date_posted' is an uncommitted server timestamp. (Since the value of this field is unknown, you cannot start/end a query with it.)
at com.google.firebase.firestore.Query.boundFromDocumentSnapshot(com.google.firebase:firebase-firestore##21.4.3:758)
at com.google.firebase.firestore.Query.startAfter(com.google.firebase:firebase-firestore##21.4.3:648)
at com.project.alihammoud.foodreviewlb.BottomSheetFragmentComment.loadMore(BottomSheetFragmentComment.java:251)
at com.project.alihammoud.foodreviewlb.BottomSheetFragmentComment$2.onScrolled(BottomSheetFragmentComment.java:191)
at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5173)
I have it that when I send a document to the firestore database, it gets its timestamp from the firestore server. I know that there is a delay in assigning the Timestamp so I tried adding a wait before refreshing but that did not work. Any ideas what could work to avoid this issue?
Here is my code:
public class BottomSheetFragmentComment extends BottomSheetDialogFragment {
private Toolbar toolbar;
private String document_id;
private String currentUserID;
private FirebaseAuth auth;
private FirebaseUser currentUser;
private FirebaseFirestore db;
private RecyclerView recyclerView;
private List<CommentInfo> comments_list;
private CommentsAdapter commentsAdapter;
private ImageButton comment_button;
private EditText comment_text;
private DocumentSnapshot lastVisible;
private Boolean isFirstPageFirstLoad = true;
private CommentID commentID;
public BottomSheetFragmentComment(){
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bottom_sheet_comments,container,false);
document_id = this.getArguments().getString("docID");
auth = FirebaseAuth.getInstance();
currentUser = auth.getCurrentUser();
currentUserID = currentUser.getUid();
db = FirebaseFirestore.getInstance();
comments_list = new ArrayList<>();
commentsAdapter = new CommentsAdapter(comments_list);
Query firstQuery = db.collection("Reviews").document(document_id).collection("Comments")
.orderBy("date_posted", Query.Direction.DESCENDING)
.limit(20);
firstQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshot, #Nullable FirebaseFirestoreException e) {
if (e == null){
if (!documentSnapshot.isEmpty()){
if (isFirstPageFirstLoad){
lastVisible = documentSnapshot.getDocuments().get(documentSnapshot.size()-1);
}
for (DocumentChange documentChange: documentSnapshot.getDocumentChanges()){
if (documentChange.getType() == DocumentChange.Type.ADDED){
String commentID = documentChange.getDocument().getId();
CommentInfo commentAdded = documentChange.getDocument().toObject(CommentInfo.class);
if (isFirstPageFirstLoad){
comments_list.add(commentAdded);
}
else {
comments_list.add(0,commentAdded);
}
commentsAdapter.notifyDataSetChanged();
}
else if (documentChange.getType() == DocumentChange.Type.REMOVED){
comments_list.remove(documentChange.getOldIndex());
commentsAdapter.notifyItemRemoved(documentChange.getOldIndex());
}
else if (documentChange.getType() == DocumentChange.Type.MODIFIED){
CommentInfo commentModified = documentChange.getDocument().toObject(CommentInfo.class);
if (documentChange.getOldIndex() == documentChange.getNewIndex()) {
// Item changed but remained in same position
comments_list.set(documentChange.getOldIndex(),commentModified);
commentsAdapter.notifyItemChanged(documentChange.getOldIndex());
}else {
// Item changed and changed position
comments_list.remove(documentChange.getOldIndex());
comments_list.add(documentChange.getNewIndex(),commentModified);
commentsAdapter.notifyItemMoved(documentChange.getOldIndex(),documentChange.getNewIndex());
}
commentsAdapter.notifyDataSetChanged();
}
}
isFirstPageFirstLoad = false;
}
}
}
});
// db.collection("Reviews").document(document_id).collection("Comments")
if (currentUser != null){
//setUpRecyclerView();
recyclerView = view.findViewById(R.id.recycle_view);
recyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getContext());
mLayoutManager.setReverseLayout(true);
// mLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(commentsAdapter);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
Boolean reachedBottom = !recyclerView.canScrollVertically(-5);
if (reachedBottom){
String desc = lastVisible.getString("comment");
Toast.makeText(getContext(),"Reached: " + desc ,Toast.LENGTH_SHORT).show();
loadMore();
/* final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
}
},5000);*/
}
}
});
}
toolbar = view.findViewById(R.id.toolbar);
toolbar.setTitle("Comments");
toolbar.setTitleTextColor(Color.BLACK);
comment_text = view.findViewById(R.id.comment_text);
comment_button = view.findViewById(R.id.comment_button);
comment_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db.collection("Reviews").document(document_id).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.getResult().exists()){
DocumentReference newComment = db.collection("Reviews").document(document_id).collection("Comments").document();
CommentInfo commentInfo = new CommentInfo();
commentInfo.setUser_id(currentUserID);
commentInfo.setComment(comment_text.getText().toString().trim());
newComment.set(commentInfo);
comment_text.getText().clear();
}
else {
Toast.makeText(getContext(), "This review has been removed, please refresh your feed.", Toast.LENGTH_LONG).show();
}
}
});
}
});
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void loadMore(){
Query nextQuery = db.collection("Reviews").document(document_id).collection("Comments")
.orderBy("date_posted", Query.Direction.DESCENDING)
.startAfter(lastVisible)
.limit(10);
nextQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshot, #Nullable FirebaseFirestoreException e) {
if (e == null){
if (!documentSnapshot.isEmpty()){
lastVisible = documentSnapshot.getDocuments().get(documentSnapshot.size()-1);
for (DocumentChange documentChange: documentSnapshot.getDocumentChanges()){
if (documentChange.getType() == DocumentChange.Type.ADDED){
String commentID = documentChange.getDocument().getId();
CommentInfo commentAdded = documentChange.getDocument().toObject(CommentInfo.class);
comments_list.add(commentAdded);
commentsAdapter.notifyDataSetChanged();
}
else if (documentChange.getType() == DocumentChange.Type.REMOVED){
comments_list.remove(documentChange.getOldIndex());
commentsAdapter.notifyItemRemoved(documentChange.getOldIndex());
}
else if (documentChange.getType() == DocumentChange.Type.MODIFIED){
// modifying
CommentInfo commentModified = documentChange.getDocument().toObject(CommentInfo.class);
if (documentChange.getOldIndex() == documentChange.getNewIndex()) {
// Item changed but remained in same position
comments_list.set(documentChange.getOldIndex(),commentModified);
commentsAdapter.notifyItemChanged(documentChange.getOldIndex());
}else {
// Item changed and changed position
comments_list.remove(documentChange.getOldIndex());
comments_list.add(documentChange.getNewIndex(),commentModified);
commentsAdapter.notifyItemMoved(documentChange.getOldIndex(),documentChange.getNewIndex());
}
commentsAdapter.notifyDataSetChanged();
}
}
}
}
}
});
}
#NonNull #Override public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
#Override public void onShow(DialogInterface dialogInterface) {
BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
setupFullHeight(bottomSheetDialog);
}
});
return dialog;
}
private void setupFullHeight(BottomSheetDialog bottomSheetDialog) {
FrameLayout bottomSheet = (FrameLayout) bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
int windowHeight = getWindowHeight();
if (layoutParams != null) {
layoutParams.height = windowHeight;
}
bottomSheet.setLayoutParams(layoutParams);
behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
private int getWindowHeight() {
// Calculate window height for fullscreen use
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels;
}
}
public class CommentsAdapter extends RecyclerView.Adapter<CommentsAdapter.ViewHolder> {
private Context context;
private FirebaseFirestore db;
public List<CommentInfo> comments_list;
public CommentsAdapter(List<CommentInfo> comments_list){
this.comments_list = comments_list;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view_comments,parent,false);
context = parent.getContext();
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull final ViewHolder holder, int position) {
db = FirebaseFirestore.getInstance();
//final String commentID = comments_list.get(position)
String comment = comments_list.get(position).getComment();
holder.setComment(comment);
final String commentUserID = comments_list.get(position).getUser_id();
DocumentReference userData = db.collection("Users").document(commentUserID);
userData.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
if (task.getResult().exists()) {
String firstname = task.getResult().getString("first_name");
String lastname = task.getResult().getString("last_name");
String userPicture = task.getResult().getString("profile_picture");
holder.setUserData(firstname,lastname,userPicture);
}
}
}
});
}
#Override
public int getItemCount() {
return comments_list.size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
private View mView;
private TextView comment, first_name, last_name;
CircleImageView users_profile_picture;
public ViewHolder(#NonNull final View itemView) {
super(itemView);
mView = itemView;
}
public void setComment(String CommentText){
comment = mView.findViewById(R.id.comment);
comment.setText(CommentText);
}
private void setUserData(String firstName, String lastName, String profilePicture){
first_name = mView.findViewById(R.id.first_name);
last_name = mView.findViewById(R.id.last_name);
users_profile_picture = mView.findViewById(R.id.users_profile_picture);
first_name.setText(firstName);
last_name.setText(lastName);
if (profilePicture != null){
Glide.with(context).load(profilePicture).into(users_profile_picture);
}
}
}
}
#IgnoreExtraProperties
public class CommentInfo {
private String user_id;
private String comment;
private #ServerTimestamp Date date_posted;
public CommentInfo(){
}
public CommentInfo(String user_id, String comment, Date date_posted) {
this.user_id = user_id;
this.comment = comment;
this.date_posted = date_posted;
}
public String getUser_id() {
return user_id;
}
public void setUser_id(String user_id) {
this.user_id = user_id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public Date getDate_posted() {
return date_posted;
}
public void setDate_posted(Date date_posted) {
this.date_posted = date_posted;
}
}
The error message is:
Invalid query. You are trying to start or end a query using a document for which the field 'date_posted' is an uncommitted server timestamp. (Since the value of this field is unknown, you cannot start/end a query with it.)
So it looks like it comes from this:
Query nextQuery = db.collection("Reviews").document(document_id).collection("Comments")
.orderBy("date_posted", Query.Direction.DESCENDING)
.startAfter(lastVisible)
.limit(10);
From the error message it seems that the date_posted field may have unknown values, in which case you can't query on it.
This comes from:
private #ServerTimestamp Date date_posted;
From this Github issue with multiple comments from Firestore team members, it seems that:
pagination and server-issued time are essentially incompatible
I'm building a Bank app and want to show history for transactions on the account, When I save the time to Firestore its format as a timestamp, but when I try to display it in my RecyclerView its just seconds and nanoseconds.
How can I show the date and time?
My recyclerView method:
private void setUpRecyclerView() {
String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
CollectionReference accountTransRef = db.collection(userId).document("accounts")
.collection("accounts").document(accountID).collection("transactions");
Query query = accountTransRef.orderBy("tTimestamp",Query.Direction.DESCENDING);
FirestoreRecyclerOptions<AccountTransactionModel> options = new FirestoreRecyclerOptions.Builder<AccountTransactionModel>()
.setQuery(query, AccountTransactionModel.class)
.build();
adapter = new AccountTransferAdapter(options);
RecyclerView recyclerView = findViewById(R.id.rwTransactionList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
My Model for Transactions
public class AccountTransactionModel {
private String tType,tAccountToId, tDocumentId;
private Timestamp tTimestamp;
private double tAmount;
public AccountTransactionModel() {
}
public AccountTransactionModel(String tType, String tAccountToId, String tDocumentId, Timestamp tTimestamp, double tAmount) {
this.tType = tType;
this.tAccountToId = tAccountToId;
this.tDocumentId = tDocumentId;
this.tTimestamp = tTimestamp;
this.tAmount = tAmount;
}
public String gettType() {
return tType;
}
public String gettAccountToId() {
return tAccountToId;
}
#Exclude
public String gettDocumentId() {
return tDocumentId;
}
public void settDocumentId(String tDocumentId) {
this.tDocumentId = tDocumentId;
}
public Timestamp gettTimestamp() {
return tTimestamp;
}
public double gettAmount() {
return tAmount;
}
}
My adapter
public class AccountTransferAdapter extends FirestoreRecyclerAdapter<AccountTransactionModel, AccountTransferAdapter.TransferHolder > {
public AccountTransferAdapter(#NonNull FirestoreRecyclerOptions<AccountTransactionModel> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull TransferHolder holder, int position, #NonNull AccountTransactionModel model) {
holder.tvTransListAmount.setText(Double.toString(model.gettAmount()));
holder.tvTransListType.setText(model.gettType());
holder.tvTransListTime.setText(model.gettTimestamp().toString());
}
#NonNull
#Override
public TransferHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.transactions_list,viewGroup,false);
return new TransferHolder(v);
}
class TransferHolder extends RecyclerView.ViewHolder{
TextView tvTransListAmount;
TextView tvTransListTime;
TextView tvTransListType;
public TransferHolder(#NonNull View itemView) {
super(itemView);
tvTransListAmount = itemView.findViewById(R.id.trans_list_amount);
tvTransListTime = itemView.findViewById(R.id.trans_list_time);
tvTransListType = itemView.findViewById(R.id.trans_list_type);
//tvAccName = itemView.findViewById(R.id.tvAccountName);
//tvAccBalance = itemView.findViewById(R.id.tvAccountBalance);
}
}
}
What is displayed in my View,App and Firestore:
Timestamp(seconds=1558437203,nanoseconds=72000000)
If Timestamp is firebase package, then you can go with Timestamp#toDate() function
model.gettTimestamp().toDate().toString() which should give you whole date
Change this:
holder.tvTransListTime.setText(model.gettTimestamp().toString());
into this:
holder.tvTransListTime.setText(model.gettTimestamp().toDate());
From the docs:
public Date toDate ()
Returns a new Date corresponding to this timestamp.
I am using recyclerView and Adapter to fetch the data in profileActivity
here is my
public class studentDetailsRecyclerActivity extends AppCompatActivity {
//recyclerview to set the details for UI in the student profile activity
private RecyclerView mRecyclerView;
private storeDetailsAdapter mStoreDetailsAdapter;
private List<storeStudentDetails> studentDetailsList;
private FirebaseFirestore dbReference;
private ProgressBar mProgressBar;
private String TAG = studentDetailsRecyclerActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
dbReference = FirebaseFirestore.getInstance();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_details);
mProgressBar = findViewById(R.id.progressbar);
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView_products);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
studentDetailsList = new ArrayList<>();
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
//to get the "details" this is our collection from firestore so we must fetch them
//by calling the addOnSuccessListener
dbReference.collection("details").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) { //we must have to hide the progress bar when the data gets loaded
//here queryDocumentsSnapshot will hold all the "details" which is your collection in firestore
if(!queryDocumentSnapshots.isEmpty()){
//we must have to create empty list so that to store all
//details from DocumentsSnapshots
List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
//enhanced for loop because we have to give every index documentSnapShot
for(DocumentSnapshot d: list){
storeStudentDetails sd = d.toObject(storeStudentDetails.class);
studentDetailsList.add(sd);
Log.d(TAG, "onSuccess: " + sd.toString());
}
//to refresh and sync we must have to use notifyDataSetChanged
mStoreDetailsAdapter.notifyDataSetChanged();
}
}
}) .addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}
}
and here is my storeDetailsAdapter
import java.util.List;
public class storeDetailsAdapter extends RecyclerView.Adapter<storeDetailsAdapter.StudentViewHolder>{
private Context context;
private List<storeStudentDetails> studentDetailsList;
public storeDetailsAdapter(Context context, List<storeStudentDetails> studentDetailsList) {
this.context = context;
this.studentDetailsList = studentDetailsList;
}
#NonNull
#Override
public StudentViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new StudentViewHolder(
LayoutInflater.from(context).inflate(R.layout.profile_activity, parent, false)
);
}
#Override
public void onBindViewHolder(#NonNull StudentViewHolder holder, int position) {
storeStudentDetails mStoreDetails = studentDetailsList.get(position);
holder.studName.setText(mStoreDetails.getStudentName());
holder.rollNum.setText(mStoreDetails.getRollNo());
holder.bookName.setText( mStoreDetails.getBook());
holder.fine.setText("Fine:" + mStoreDetails.getFine());
holder.dept.setText(mStoreDetails.getDept());
}
#Override
public int getItemCount() {
return studentDetailsList.size();
}
class StudentViewHolder extends RecyclerView.ViewHolder {
TextView studName,rollNum,bookName,dept,fine;
public StudentViewHolder(View itemView) {
super(itemView);
studName=itemView.findViewById(R.id.studentName_prof);
rollNum = itemView.findViewById(R.id.rollNumber_prof);
bookName = itemView.findViewById(R.id.bookName_prof);
fine = itemView.findViewById(R.id.fineAmt_prof);
dept = itemView.findViewById(R.id.department_prof);
}
}
}
and here is my StoreStudentDetails class:
public class storeStudentDetails implements Serializable {
private String studentName;
private String rollNo;
private String book;
private Double fine;
private String dept;
#Exclude private String id;
public storeStudentDetails() {
}
public storeStudentDetails(String studentName, String rollNo,String book, double fine ,String dept) {
this.studentName = studentName;
this.rollNo = rollNo;
this.book = book;
this.fine = fine;
this.dept = dept;
}
public void setId(String id) {
this.id = id;
}
public String getStudentName() {
return studentName;
}
public String getRollNo() {
return rollNo;
}
public String getBook() {
return book;
}
public Double getFine() {
return fine;
}
public String getDept() {
return dept;
}
public String getId() {
return id;
}
}
To solve this, please move the following lines of code:
mStoreDetailsAdapter = new storeDetailsAdapter(this,studentDetailsList);
mRecyclerView.setAdapter(mStoreDetailsAdapter);
Right before the following line of code:
mStoreDetailsAdapter.notifyDataSetChanged();
And this is because onSuccess() method has an asynchronous behavior and by the time you are setting the adapter outside the callback your list is empty.
As you can see, the easiest solution for this problem is to move those lines of code inside the callback. but if you want to use the value of your studentDetailsList outside the onSuccess() method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I'm working on a project with android and firebase real-time database.
I'm not really sure how to achieve this structure for my project.
I have a list of Users (with name, email etc).
I want to add one/or multiple item(s) for a single user.
So User1 can have:
Item1(color: Black, percentage: 90 etc etc)
Item2(...)
I am not sure if this is the correct way to structure the data or if there is a better way.
Firebase structure
And I should be able to get all items for this user and show them in a listview.
Any help how to achieve this.
I would advice you to work with RecyclerView.
The following example is used as a chat:
Firstly create your ViewHolder and Data class:
public static class FirechatMsgViewHolder extends RecyclerView.ViewHolder {
TextView userTextView;
TextView emailUserTextView;
TextView msgTextView;
CircleImageView userImageView;
public FirechatMsgViewHolder(View v) {
super(v);
userTextView = (TextView) itemView.findViewById(R.id.userTextView);
emailUserTextView = (TextView) itemView.findViewById(R.id.emailUserTextView);
msgTextView = (TextView) itemView.findViewById(R.id.msgTextView);
userImageView = (CircleImageView) itemView.findViewById(R.id.userImageView);
}
}
Data Class:
public class ChatMessage {
private String text;
private String name;
private String email;
private String photoUrl;
public ChatMessage() {
}
public ChatMessage(String name, String email, String text, String photoUrl) {
this.text = text;
this.name = name;
this.photoUrl = photoUrl;
this.email = email;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhotoUrl() {
return photoUrl;
}
public void setPhotoUrl(String photoUrl) {
this.photoUrl = photoUrl;
}
}
Then add these fields into the ChatActivity:
private DatabaseReference mSimpleFirechatDatabaseReference;
private FirebaseRecyclerAdapter<ChatMessage, FirechatMsgViewHolder>
mFirebaseAdapter;
Then init all fields in your onCreate + set adapter:
//Create the reference to get data from Firebase.
mSimpleFirechatDatabaseReference = FirebaseDatabase.getInstance().getReference();
//Fill the adapter with data + add all required listeners
mFirebaseAdapter = new FirebaseRecyclerAdapter<ChatMessage,
FirechatMsgViewHolder>(
ChatMessage.class,
R.layout.chat_message,
FirechatMsgViewHolder.class,
mSimpleFirechatDatabaseReference.child("messages")) {
#Override
protected void populateViewHolder(FirechatMsgViewHolder viewHolder, ChatMessage friendlyMessage, int position) {
mProgressBar.setVisibility(ProgressBar.INVISIBLE);
viewHolder.userTextView.setText(friendlyMessage.getName());
viewHolder.emailUserTextView.setText(friendlyMessage.getEmail());
viewHolder.msgTextView.setText(friendlyMessage.getText());
if (friendlyMessage.getPhotoUrl() == null) {
viewHolder.userImageView
.setImageDrawable(ContextCompat
.getDrawable(ChatActivity.this,
R.drawable.profile));
} else {
Glide.with(ChatActivity.this)
.load(friendlyMessage.getPhotoUrl())
.into(viewHolder.userImageView);
}
}
};
mFirebaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
int chatMessageCount = mFirebaseAdapter.getItemCount();
int lastVisiblePosition =
mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
if (lastVisiblePosition == -1 ||
(positionStart >= (chatMessageCount - 1) &&
lastVisiblePosition == (positionStart - 1))) {
mMessageRecyclerView.scrollToPosition(positionStart);
}
}
});
How to send data to Firebase
// The way to send data to the database. Add any required path!
mSendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ChatMessage friendlyMessage = new
ChatMessage(mUsername,
mUseremail,
"Some text",
mPhotoUrl);
mSimpleFirechatDatabaseReference.child("messages")
.push().setValue(friendlyMessage);
}
});
You should structure your database as shown in below image. With this structure, you can get all items for a particular User and easily show them in ListView.