1

I'm able to add a message to firestore when I clicked the send button in the chatroom, but the recyclerview doesn't update in realtime so the new message doesn't appear. The message does appear when I reopen the chatroom which isn't best pratice. Normally FirestoreRecyclerAdapter should automatically updated the query when changes are made and update the recyclerview. I'm working in Android Studio with Java and using fragments instead of activities.

ChatRoomFragment:

public class ChatRoomFragment extends Fragment {
    private static final String TAG = "Notable:ChatRoom";
    private FirebaseFirestore db;
    private Query query;
    private FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> adapter;
    private FirebaseAuth mAuth;
    private FirebaseUser currentUser;

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.chatroom_fragment, container, false);
        MaterialToolbar topBar = view.findViewById(R.id.topAppBar);
        Button sendButton = view.findViewById(R.id.send_message);
        EditText input = view.findViewById(R.id.message_edit_text);
        RecyclerView chatRecyclerView = view.findViewById(R.id.chat_recyclerview);
        mAuth = FirebaseAuth.getInstance();
        currentUser = mAuth.getCurrentUser();
        db = FirebaseFirestore.getInstance();
        //FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder().setPersistenceEnabled(true).setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED).build();
        //db.setFirestoreSettings(settings);
        String key = mAuth.getCurrentUser().getUid();
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity().getApplicationContext());
        layoutManager.setStackFromEnd(true);
        chatRecyclerView.setLayoutManager(layoutManager);

        String title = this.getArguments().getString("buttonText");
        topBar.setTitle(title);

        query = db.collection("messages").whereEqualTo("chatGroup", title).orderBy("messageTime");
        adapter = new MessageAdapter(getActivity(), query, key);
        //https://medium.com/@akhilkc9/simple-android-chat-application-using-firestorerecycleradapter-7f632da2eaee
        adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onItemRangeChanged(int positionStart, int itemCount) {
                super.onItemRangeChanged(positionStart, itemCount);
                chatRecyclerView.scrollToPosition(itemCount);
            }
        });
        chatRecyclerView.setAdapter(adapter);

        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String text = input.getText().toString();
                if(!text.equals("")){
                    db.collection("users").document(key).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
                        @Override
                        public void onSuccess(DocumentSnapshot documentSnapshot) {
                            if (documentSnapshot.exists()){
                                String firstName = documentSnapshot.get("firstname").toString();
                                db.collection("messages").add(new Message(firstName, text, key, title));
                            }else{
                                Log.w(TAG, "No such document");
                            }
                        }
                    });
                }
                input.setText("");
            }
        });

        return view;
    }

    @Override
    public void onStart() {
        super.onStart();
        if(adapter!=null) adapter.startListening();
    }

    @Override
    public void onStop() {
        super.onStop();
        if(adapter!=null) adapter.stopListening();
    }
}

MessageAdapter:

public class MessageAdapter extends FirestoreRecyclerAdapter<Message, MessageAdapter.MessageHolder> {
    private final String TAG = "MessageAdapter";
    Context context;
    String userId;
    StorageReference storageReference;
    private RequestOptions requestOptions = new RequestOptions();
    private final int MESSAGE_IN_VIEW_TYPE  = 1;
    private final int MESSAGE_OUT_VIEW_TYPE = 2;

    public MessageAdapter(@NonNull Context context, Query query, String userID) {
        super(new FirestoreRecyclerOptions.Builder<Message>().setQuery(query, Message.class).build());
        this.context = context;
        this.userId = userID;
    }

    @Override
    public int getItemViewType(int position) {
        if(getItem(position).getMessageUserId().equals(userId)){
            return MESSAGE_IN_VIEW_TYPE;
        }
        return MESSAGE_OUT_VIEW_TYPE;
    }

    @Override
    public MessageHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = null;
        if(viewType==MESSAGE_IN_VIEW_TYPE){
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.chat_item, parent, false);
        }else{
            view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.chat_item_other, parent, false);
        }
        return new MessageHolder(view);
    }

    //Voordeel van Recyclerview is dat het automatisch een Viewholder heeft, dit heeft gridview niet automatisch
    @Override
    protected void onBindViewHolder(@NonNull MessageHolder holder, int position, @NonNull Message model) {
        final TextView mText = holder.mText;
        final TextView mUsername = holder.mUsername;
        final TextView mTime = holder.mTime;
        final TextView mDate = holder.mDate;
        final ImageView imgProfile = holder.imgProfile;

        if(mUsername != null && imgProfile != null){
            mUsername.setText(model.getMessageUser());
            //Glide.with(context).setDefaultRequestOptions(requestOptions).load(R.drawable.placeholder).diskCacheStrategy(DiskCacheStrategy.DATA).into(imgProfile);
            //storageReference.child(model.getMessageUserId())
        }
        mText.setText(model.getMessageText());
        mDate.setText(DateFormat.format("dd MMM", model.getMessageTime()));
        mTime.setText(DateFormat.format("h:mm", model.getMessageTime()));

    }

    static class MessageHolder extends RecyclerView.ViewHolder {

        TextView mText;
        TextView mUsername;
        TextView mTime;
        TextView mDate;
        ImageView imgProfile;

        public MessageHolder(View itemView) {
            super(itemView);
            mText = itemView.findViewById(R.id.message_text);
            mUsername = itemView.findViewById(R.id.message_user);
            mTime = itemView.findViewById(R.id.message_time);
            mDate = itemView.findViewById(R.id.message_date);
            imgProfile = itemView.findViewById(R.id.imgDps);
        }
    }
}

chatroom_fragment.xml :

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/layout_group_chat"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true"
        android:orientation="vertical"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/topAppBar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="@string/chat_group"
            style="@style/Widget.MaterialComponents.Toolbar.Primary" />
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/chat_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="16dp"
        app:layout_constrainedHeight="true"
        app:layout_constraintBottom_toTopOf="@+id/text_indicator"
        app:layout_constraintTop_toBottomOf="@+id/layout_group_chat" />

    <TextView
        android:id="@+id/text_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:visibility="gone"
        app:layout_constraintBottom_toTopOf="@id/view"
        app:layout_constraintStart_toStartOf="parent" />

    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#e0e0e0"
        app:layout_constraintBottom_toTopOf="@+id/layout_chatbox" />

    <RelativeLayout
        android:id="@+id/layout_chatbox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent">


        <EditText
            android:layout_marginStart="16dp"
            android:id="@+id/message_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:background="@android:color/transparent"
            android:hint="@string/hint_message"
            android:inputType="text"
            android:maxLines="6"
            tools:ignore="Autofill" />

        <Button
            android:id="@+id/send_message"
            android:layout_width="74dp"
            android:layout_height="48dp"
            android:layout_alignParentEnd="true"
            android:background="?attr/selectableItemBackground"
            android:text="@string/send"
            android:textColor="@color/design_default_color_primary" />

    </RelativeLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

1 Answers1

1

Fixed the problem, it was because I didn't add an index for the query on firestore. I got a error from the android studio console (and firestore) that explained it see link:

https://coderedirect.com/questions/651254/firebase-firestore-query-error-9-failed-precondition-the-query-requires-an-i