I'm trying to create chatting service for my project, in order to do that I store my messages in a Firestore database that I fetch from the server and assign the text comparing the sender id to current user id.
When I start the activity, the messages show properly, but when I scroll down, either my messages stop showing up, or other user's messages disappear, depending whether on which account the chat is opened.
I can't seem to figure out why that happens.
My readMessages function:
private void readMessages(){
Query query = DataRef.collection("rooms").document(roomID).collection("messages").orderBy("timestamp", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<MessageModel> options = new FirestoreRecyclerOptions.Builder<MessageModel>().setQuery(query, MessageModel.class).build();
adapter = new FirestoreRecyclerAdapter<MessageModel, MessageAdapter.ViewHolder>(options) {
#Override
protected void onBindViewHolder(MessageAdapter.ViewHolder holder, int position, MessageModel model) {
if (myUid.equals(model.getUid())) {
Log.d(TAG, "status1");
holder.msg_right.setText(String.valueOf(model.getMsg()));
holder.msg_left.setVisibility(View.INVISIBLE);
holder.profile_image.setVisibility(View.INVISIBLE);
holder.msg_nick.setVisibility(View.INVISIBLE);
}
else
{
DataRef.collection("users").document(String.valueOf(model.getUid())).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot document = task.getResult();
msgNick = document.getString("usernm");
}
});
holder.msg_nick.setText(msgNick);
holder.msg_left.setText(String.valueOf(model.getMsg()));
holder.msg_right.setVisibility(View.INVISIBLE);
Log.d(TAG, "status0" );
}
}
#NonNull
#Override
public MessageAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType){
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message,parent,false);
return new MessageAdapter.ViewHolder(v);
}
};
adapter.startListening();
recyclerView.setAdapter(adapter);
}
My message adapter class:
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return 0;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView msg_right;
public ImageView profile_image;
public TextView msg_left;
public TextView msg_nick;
public ViewHolder(View itemView) {
super(itemView);
msg_right = itemView.findViewById(R.id.msg_right);
profile_image = itemView.findViewById(R.id.msg_leftavatar);
msg_left = itemView.findViewById(R.id.msg_left);
msg_nick = itemView.findViewById(R.id.msg_nick);
}
}
}
State of the chat before scrolling down and up:
"
And after doing so:
Please try to move the blow block of code of setting the holder members within the callback listener in else statement.
private void readMessages(){
Query query = DataRef.collection("rooms").document(roomID).collection("messages").orderBy("timestamp", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<MessageModel> options = new FirestoreRecyclerOptions.Builder<MessageModel>().setQuery(query, MessageModel.class).build();
adapter = new FirestoreRecyclerAdapter<MessageModel, MessageAdapter.ViewHolder>(options) {
#Override
protected void onBindViewHolder(MessageAdapter.ViewHolder holder, int position, MessageModel model) {
if (myUid.equals(model.getUid())) {
Log.d(TAG, "status1");
holder.msg_right.setText(String.valueOf(model.getMsg()));
holder.msg_left.setVisibility(View.INVISIBLE);
holder.profile_image.setVisibility(View.INVISIBLE);
holder.msg_nick.setVisibility(View.INVISIBLE);
}
else
{
DataRef.collection("users").document(String.valueOf(model.getUid())).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot document = task.getResult();
msgNick = document.getString("usernm");
holder.msg_nick.setText(msgNick);
holder.msg_left.setText(String.valueOf(model.getMsg()));
holder.msg_right.setVisibility(View.INVISIBLE);
Log.d(TAG, "status0" ); }
});
}
}
Related
Im creating an app that shows user recipe based on ingredient they have using SpoonAcular API
The app fetched the ingredient user currently have on Firebase Realtime Database and using the data to fetch the recipe. I've created all the adapter needed to fetch the data but the app not showing the recyclerView as its should. Here's my code
RequestManager.java
public class RequestManager {
Context context;
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.spoonacular.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
public RequestManager(Context context){
this.context = context;
}
public void getRecipeByIngredient(RecipeByIngredientListener listener, List <String> IngredientList){
CallRecipeByIngredient callRecipeByIngredient = retrofit.create(CallRecipeByIngredient.class);
Call <List<RecipeIngredResponse>> call = callRecipeByIngredient.callRecipeByIngredient(context.getString(R.string.API), IngredientList, "20");
call.enqueue(new Callback<List<RecipeIngredResponse>>() {
#Override
public void onResponse(Call<List<RecipeIngredResponse>> call, Response<List<RecipeIngredResponse>> response) {
if(!response.isSuccessful()){
listener.didError(response.message());
return;
}
listener.didFetch(response.body(), response.message());
}
#Override
public void onFailure(Call<List<RecipeIngredResponse>> call, Throwable t) {
listener.didError(t.getMessage());
}
});
}
private interface CallRecipeByIngredient{
#GET("recipes/findByIngredients")
Call<List<RecipeIngredResponse>> callRecipeByIngredient(
#Query("apiKey") String apiKey,
#Query("ingredients") List <String> Ingredient,
#Query("number") String number
);
}
}
RecipeByIngredientListener
public interface RecipeByIngredientListener {
void didFetch(List<RecipeIngredResponse> response, String message);
void didError(String message);
}
RecipeByIngredientAdapter
public class RecipeByIngredientAdapter extends RecyclerView.Adapter<RecipeByIngredientViewHolder> {
Context context;
List<RecipeIngredResponse> list;
public RecipeByIngredientAdapter(Context context, List<RecipeIngredResponse> list) {
this.context = context;
this.list = list;
}
#NonNull
#Override
public RecipeByIngredientViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new RecipeByIngredientViewHolder(LayoutInflater.from(context).inflate(R.layout.list_recipebyingred , parent, false));
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull RecipeByIngredientViewHolder holder, int position) {
holder.recipeName.setText(list.get(position).title);
holder.missedIngred.setText(list.get(position).missedIngredientCount + " Missed Ingredient");
holder.likes.setText(list.get(position).likes + " Likes");
Picasso.get().load(list.get(position).image).into(holder.recipeImage);
}
#Override
public int getItemCount() {
return list.size();
}
}
class RecipeByIngredientViewHolder extends RecyclerView.ViewHolder{
ImageView recipeImage;
TextView recipeName, missedIngred, likes;
public RecipeByIngredientViewHolder(#NonNull View itemView) {
super(itemView);
recipeImage = itemView.findViewById(R.id.recipeImage);
recipeName = itemView.findViewById(R.id.recipeName);
missedIngred = itemView.findViewById(R.id.missedIngred);
likes = itemView.findViewById(R.id.likes);
}
}
Home.java (Fragment)
RandomRecipeAdapter randomRecipeAdapter;
RequestManager manager;
RecyclerView recyclerView, recyclerFromYourFridge;
RecipeByIngredientAdapter recipeByIngredientAdapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_home, container, false);
manager = new RequestManager(getContext());
manager.getRandomRecipe(randomRecipeResponseListener);
recyclerFromYourFridge = (RecyclerView) v.findViewById(R.id.recyclerFromYourFridge);
//get ingredient from database
List<String> Ingredient = new ArrayList<>();
FirebaseAuth mAuth = FirebaseAuth.getInstance();
String currentUser = mAuth.getCurrentUser().getUid();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference().child("Ingredient").child(currentUser);
reference.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if(task.isSuccessful()){
for (DataSnapshot userSnapshot : task.getResult().getChildren()){
Ingredient.add(userSnapshot.getKey());
}
}
}
});
manager.getRecipeByIngredient(recipeByIngredientListener , Ingredient);
recyclerView = (RecyclerView) v.findViewById(R.id.randomRecipeRecycler);
return v;
}
private final RecipeByIngredientListener recipeByIngredientListener = new RecipeByIngredientListener() {
#Override
public void didFetch(List<RecipeIngredResponse> response, String message) {
recyclerFromYourFridge.setHasFixedSize(true);
recyclerFromYourFridge.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
recipeByIngredientAdapter = new RecipeByIngredientAdapter(getContext(), response);
recyclerFromYourFridge.setAdapter(recipeByIngredientAdapter);
}
#Override
public void didError(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
};
Pretty sure there's nothing wrong with the app fetching ingredient from Firebase cause i've tested it and the api capable to get recipe by using ArrayList. Is there anything wrong with this code?
The issue is because calls to the Firebase database are asynchronous, if you call the value outside of addOnCompleteListener can be empty
Change the reference.get().addOnCompleteListener to:
reference.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if(task.isSuccessful()){
List<String> Ingredient = new ArrayList<>();
for (DataSnapshot userSnapshot : task.getResult().getChildren()){
Ingredient.add(userSnapshot.getKey());
}
manager.getRecipeByIngredient(recipeByIngredientListener , Ingredient);
}
}
});
I have passed data from my Activity to My Adapter. When I debug, I can see the correct data has successfully passed to my adapter, but when I attempt to use it as a string ( for example, if I want to set the text as the data I just passed), it shows as null.
On the line that says " this.uniquesharedIds = uniquesharedId;" - the "uniqiuesharedIds" is showing as null.
"uniquesharedId" shows has the successfully passed data.
I need to be able to use the string of "uniqiuesharedIds."
Sorry if this is a silly question. Sending data from Activities to Adapters always confuses me and Im not able to find a ton of documentation/videos on the topic. Thank you.
My Activity In the On Create Method
myadapter = new Invite_Contributors_Adapter(contributorInviteList, getIntent().getStringExtra("uniquesharedId"));
The Adapter
public class Invite_Contributors_Adapter extends RecyclerView.Adapter<Invite_Contributors_Adapter.myviewholder> {
private ArrayList<Model_Invite_Contributors_List> model_invite_contributors_lists = new ArrayList<>();
FirebaseAuth mAuth;
private FirebaseUser currentuser;
private DatabaseReference UsersReference;
Context context;
String uniquesharedIds;
private InviteContributorsInterface inviteContributorsInterface;
public Invite_Contributors_Adapter() {
}
public void updateInviteList (ArrayList list) {
model_invite_contributors_lists .clear();
model_invite_contributors_lists .addAll(list);
notifyDataSetChanged();
}
public Invite_Contributors_Adapter(ArrayList<Model_Invite_Contributors_List>model_invite_contributors_lists, String uniquesharedId) {
this.model_invite_contributors_lists = model_invite_contributors_lists;
this.uniquesharedIds = uniquesharedId;
}
#NonNull
#Override
public Invite_Contributors_Adapter.myviewholder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout_invite_contributors_list, parent, false);
return new myviewholder(view);
}
#Override
public void onBindViewHolder(#NonNull myviewholder holder, int position) {
holder.setData(model_invite_contributors_lists.get(position));
mAuth = FirebaseAuth.getInstance();
holder.Name.setText(uniquesharedIds);
}
#Override
public int getItemCount() {
return model_invite_contributors_lists.size();
}
static class myviewholder extends RecyclerView.ViewHolder implements DialogInterface.OnClickListener {
TextView Name;
CircleImageView profileImageView;
public myviewholder(#NonNull View itemView) {
super(itemView);
Name = itemView.findViewById(R.id.contributor_name);
profileImageView = itemView.findViewById(R.id.member_profile_picture);
}
#Override
public void onClick(DialogInterface dialog, int which) {
}
public void setData(Model_Invite_Contributors_List model) {
FirebaseUser currentuser;
currentuser = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference NameRef = FirebaseDatabase.getInstance().getReference(Strings.UsersReference);
NameRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//this is very important. this says to not show the current user in the list of people to invite as a contributor.//
if (currentuser.getUid().equals(model.getUser_Id())){
ViewGroup.LayoutParams params = itemView.getLayoutParams();
params.height = 0;
itemView.setLayoutParams(params);
} else {
for(DataSnapshot ds : dataSnapshot.getChildren())
{
itemView.setVisibility(View.VISIBLE);
String profileImageString;
profileImageString = model.getProfileimage();
Glide.with(profileImageView.getContext()) //pulling in image and telling the image which imageview to go to once it comes in from the database
.load(profileImageString)
.placeholder(R.drawable.circle_placeholder)
.error(R.drawable.circle_placeholder)
.into(profileImageView);
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
public void setInterface (Invite_Contributors_Adapter.InviteContributorsInterface inviteContributorsInterface) {
this.inviteContributorsInterface = inviteContributorsInterface;
}
public interface InviteContributorsInterface{
}
}
Are you using any primary constructor of the adapter like this?
myadapter = new Invite_Contributors_Adapter();
If yes, then that's where the problem is. If you're initializing object with two different constructors then you'll get the value of the object which you initialized later.
Make sure to check the adapter object & then proceed.
I want to make search data on my app based on Firebase. I've followed instructions on internet how to make search Firebase.
It shows no error, but when I clicked search button, my recycleview is empty, no data showed.
Here is my code:
btnSearch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String searchText = search_cust.getText().toString();
firebaseCustSearch(searchText);
}
});
This is my firebase search method
private void firebaseCustSearch(String searchText) {
Query firebaseSearchQuery = databaseCustomer.orderByChild("custName").startAt(searchText).endAt(searchText + "\uf8ff");
FirebaseRecyclerOptions customerOptions = new FirebaseRecyclerOptions.Builder<Customer>()
.setQuery(firebaseSearchQuery, Customer.class).build();
adapter = new FirebaseRecyclerAdapter<Customer, CustomerList.ViewHolder>(customerOptions) {
#Override
protected void onBindViewHolder(#NonNull CustomerList.ViewHolder holder, int position, #NonNull Customer model) {
holder.setname(model.getCustName());
holder.setaddress(model.getCustAddress());
Toast.makeText(CRUDCustomer.this, model.custName,Toast.LENGTH_SHORT).show();
}
#NonNull
#Override
public CustomerList.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return null;
}
};
listViewCustomer.setAdapter(adapter);
listViewCustomer.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
}
ViewHolder
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Customer customer = customerList.get(position);
holder.textName.setText(customer.getCustName());
holder.textAddress.setText(customer.getCustAddress());
holder.textPhone.setText(customer.getCustPhone());
}
public class ViewHolder extends RecyclerView.ViewHolder{
public TextView textName ;
public TextView textAddress;
public TextView textPhone ;
public ViewHolder(#NonNull View view){
super(view);
this.textName = (TextView)view.findViewById(R.id.cust_name);
this.textAddress = (TextView)view.findViewById(R.id.cust_address);
this.textPhone = (TextView)view.findViewById(R.id.cust_phone);
}
public void setname(String name){
textName.setText(name);
}
public void setaddress(String address){
textAddress.setText(address);
}
}
private void search(String text) {
List filterdNames = new ArrayList<>();
for (Modelclass s : modelclass) {
if (s.getname.contains(text)) {
search.add(s);
}
}
//calling a method of the adapter class and passing the filtered list
sampleAdapter.filterList(filterdNames);
}
I'm creating an Android app, the data that i'm sending through intent is being retrieved every time i click on the item.
I'm sending the retrieved data(which it's a subcollection that is being retrieved every time i click on item) through the intent,and all data receives in an arraylist, so the listener don't know if the same data existed in the arraylist,because the data are in the other activity.
when i click for the first time the data displayed normally in ItemMarkerActivity but when i go back and click again on the same item i see the data load again in the recycler view,and added to the previous same data, i'm using the technique of removing the data onStop but it didn't work perfectly,because i need to close all activities to see that the data removed, i tried to send the CollectionReference through intent but i couldn't do. so I need a way of removing the data immediately after closing the activity, and if anyone has another approach for solving this problem it would better.
Thanks in advance
adapter.setOnItemClickListener(new MarketAdapterRecyclerView.OnItemClickListener() {
#Override
public void onItemClick(DocumentSnapshot documentSnapshot, int position) {
CollectionReference path = documentSnapshot.getReference().collection("ShoppingItems");
listener = path.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
return;
}
for (DocumentChange dc : queryDocumentSnapshots.getDocumentChanges()) {
if (dc.getType() == DocumentChange.Type.ADDED) {
item = dc.getDocument().toObject(Item.class);
itemList.add(item);
}
}
Intent intent = new Intent (shoppingActivity.this, ItemMarkerActivity.class);
Log.v(TAG,"###################################" + itemList.toString());
intent.putExtra("keyName", itemList);
startActivity(intent);
}
});
}
}
The Activity That Receives The data
The Manifest
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> implements Parcelable{
public ArrayList<Item> ItemList;
public Context mContext;
private onMallListener mOnMallListener;
private static final int NO_POSITION = -1;
public ItemAdapter(ArrayList<Item> ItemList, Context mContext, onMallListener mOnMallListener) {
this.ItemList = ItemList;
this.mContext = mContext;
this.mOnMallListener = mOnMallListener;
}
protected ItemAdapter(Parcel in) {
ItemList = in.createTypedArrayList(Item.CREATOR);
}
public static final Creator<ItemAdapter> CREATOR = new Creator<ItemAdapter>() {
#Override
public ItemAdapter createFromParcel(Parcel in) {
return new ItemAdapter(in);
}
#Override
public ItemAdapter[] newArray(int size) {
return new ItemAdapter[size];
}
};
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.activity_card_view_item, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view, mOnMallListener);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
Item item = ItemList.get(i);
viewHolder.itemType.setText(ItemList.get(i).getItemType());
Picasso.with(mContext)
.load(item.getImageUrl())
.fit()
.centerCrop().into(viewHolder.imageUrl);
}
#Override
public int getItemCount() {
return ItemList.size();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeTypedList(ItemList);
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
View mView;
public TextView price;
public TextView description;
public TextView itemType;
public ImageView imageUrl;
onMallListener onMallListener;
public ViewHolder(#NonNull View itemView, onMallListener mOnMallListener) {
super(itemView);
mView = itemView;
itemType = (TextView) mView.findViewById(R.id.card_view_image_title);
imageUrl = (ImageView) mView.findViewById(R.id.card_view_image);
this.onMallListener = mOnMallListener;
mView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
if(mOnMallListener != null){
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION){
mOnMallListener.onMallClick(position);
}
}
}
}
public interface onMallListener{
void onMallClick(int position);
}
}
Save data using Room database in first activity and retrieve it in second.
In any place of your code (and in any activity's callback) you can clean db and all lists/recyclers which listen this data.
https://developer.android.com/training/data-storage/room
Hope it'll help
I am trying to retrieve images and text from Firebase to my Recycle view and this works well. The only problem I'm experiencing is the images are not being loaded if I login with different phones but the text data are being loaded. Any help would be appreciated.
Gets data from Firebase
//Retrieves information stored inside Post node...
public void fetchUserInfo() {
postRef = FirebaseDatabase.getInstance().getReference().child("Post");
postRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
value = ds.getValue(Post.class);
postList.add(value);
}
adapter = new Adapter(Shop_Activity.this, postList);
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.i("Error", databaseError.toString());
}
});
}
}
In this method I saved the necessary information inside a dictionary and uploaded it to Firebase
//saves user image and description inside firebase
public void saveToFirebase(){
String userId = mAuth.getCurrentUser().getUid();
postDictionary.put("desc", descriptionEditText.getText().toString());
postDictionary.put("image", selectedImageUri.toString());
postDictionary.put( "id",userId);
productsDatabaseRef.child("Post").push().setValue(postDictionary);
Intent intent = new Intent(Upload_Post.this, Shop_Activity.class);
startActivity(intent);
}
Adapter
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
Context context;
ArrayList<Post> userPost;
public Adapter(Context context, ArrayList<Post> userPost){
this.context = context;
this.userPost = userPost;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.shop_layout_design,viewGroup, false));
}
//this is where you set the value for the ui elements
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
viewHolder.desc.setText(userPost.get(i).getdesc());
Glide.with(this.context).load(userPost.get(i).getimage()).into(viewHolder.image);
//Picasso.get().load(userPost.get(i).getimage()).into(viewHolder.image);
}
#Override
public int getItemCount() {
return userPost.size();
}
//links up ui elements
class ViewHolder extends RecyclerView.ViewHolder{
TextView desc;
TextView id;
ImageView image;
public ViewHolder(#NonNull View itemView) {
super(itemView);
id = itemView.findViewById(R.id.post_title);
desc = itemView.findViewById(R.id.post_desc);
image = itemView.findViewById(R.id.post_image);
}
}
}