Search code examples
androidfirebasefirebase-realtime-databasefirebaseui

Firebase UI - custom SnapshotParser is skipped by compiler


So, I am using Firebase UI to get data from Database. It looks like this: Database

I want to iterate through "messages" to get every child, and show it as message at my app. This is how my UserMessage class looks alike:

public class UserMessage {

private String mUserLogin;
private String mUserMessage;
private String mUID;

UserMessage(String userLogin, String userMessage, String uid) {
    mUserLogin = userLogin;
    mUserMessage = userMessage;
    mUID = uid;
}

public String getmUserLogin() {
    return mUserLogin;
}

public void setmUserLogin(String mUserLogin) {
    this.mUserLogin = mUserLogin;
}

public String getmUserMessage() {
    return mUserMessage;
}

public String getmUID() {
    return mUID;
}

public void setmUID(String mUID) {
    this.mUID = mUID;
}


public void setmUserMessage(String mUserMessage) {
    this.mUserMessage = mUserMessage;
}

}

And i want to get it through FirebaseRecyclerOptions:

FirebaseRecyclerOptions<UserMessage> options =
            new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(firebaseDatabaseModel.getQuery(), new SnapshotParser<UserMessage>() {
                @NonNull
                @Override
                public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                    Log.d("snapshot length", String.valueOf(snapshot.getChildrenCount()));
                    return snapshot.getValue(UserMessage.class);
                }
            })
            .build();

And here is how my firebaseDatabaseModel.getQuery() method looks like:

 public Query getQuery() {
DatabaseReference messageReference =    database.child("messages");
    return messageReference.limitToLast(50);
}

And this is how I want to set data in my FirebaseRecyclerAdapter

class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
private UserMessage[] userMessages;

/**
 * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
 * {@link FirebaseRecyclerOptions} for configuration options.
 *
 * @param options
 */
public ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
    super(options);
    userMessages = (UserMessage[]) options.getSnapshots().toArray();
       }

@Override
protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {

    holder.message.setText(userMessages[position].getmUserMessage());
    holder.user.setText(userMessages[position].getmUserLogin());

}

@NonNull
@Override
public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
    return new ChatHolder(view);
}

static class ChatHolder extends RecyclerView.ViewHolder {

    @BindView(R.id.user_message)
    TextView message;
    @BindView(R.id.user_login)
    TextView user;

    private ChatHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(itemView);

    }
}

}

The thing is, not matter how I'm trying to send options (User Messages) through array to FirebaseRecyclerAdapter, it always has 0 count. Probably the problem is lying under custom parser (I have to compose it properly), but my debugger is just skipping this part of code:

 public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                Log.d("snapshot length", String.valueOf(snapshot.getChildrenCount()));
                return snapshot.getValue(UserMessage.class);
            }

So I cannot even check what I'm doing wrong here. I've tried to figure it out for few hours already, and I just really don't know how to deal with it.

//Edit 1: Due to @Alex Malmo suggestions, I've changed database keys to: enter image description here

And UserMessage.class to:

public class UserMessage {
private String userLogin, userMessage, uid;

public UserMessage() {}

public UserMessage(String userLogin, String userMessage, String uid) {
    this.userLogin = userLogin;
    this.userMessage = userMessage;
    this.uid = uid;
}

public String getUserLogin() { return userLogin; }
public String getUserMessage() { return userMessage; }
public String getUid() { return uid; }

}

It looks like the thing is still in FirebaseRecyclerOptions creation. I've tried those two methods:

FirebaseRecyclerOptions<UserMessage> options = new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(query,
                    UserMessage.class)
            .build();

and

    FirebaseRecyclerOptions<UserMessage> options = new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(query,
                    new SnapshotParser<UserMessage>() {
                        @NonNull
                        @Override
                        public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                            return snapshot.getValue(UserMessage.class);
                        }
                    })
            .build();

In both cases - second argument in setQuery method (parsing part) seems to be ignored (compilator just skips that part - it gets correct query, and goes straight to .build()), which seems to be a whole problem with empty data in adapter.


Solution

  • Okay, I have finally resolved it. The problem was, that I've completely misunderstood FirebaseRecyclerOptions functions. I was completely sure that it's passing data inside it, and whole thing was that I was trying to get that data, without letting whole method do its work.

    So I have changed adapter construction from:

     class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
    private UserMessage[] userMessages;
    
    /**
     * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
     * {@link FirebaseRecyclerOptions} for configuration options.
     *
     * @param options
     */
    public ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
        super(options);
        userMessages = (UserMessage[]) options.getSnapshots().toArray();
           }
    
    @Override
    protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {
    
        holder.message.setText(userMessages[position].getmUserMessage());
        holder.user.setText(userMessages[position].getmUserLogin());
    
    }
    
    @NonNull
    @Override
    public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
        return new ChatHolder(view);
    }
    
    static class ChatHolder extends RecyclerView.ViewHolder {
    
        @BindView(R.id.user_message)
        TextView message;
        @BindView(R.id.user_login)
        TextView user;
    
        private ChatHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(itemView);
    
        }
    }
    

    To:

    class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
        private ArrayList<UserMessage> messagesList = new ArrayList<>();
    
        /**
         * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
         * {@link FirebaseRecyclerOptions} for configuration options.
         *
         * @param options
         */
        ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
            super(options);           }
    
        @Override
        protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {
            messagesList.add(model);
            holder.message.setText(messagesList.get(position).getUserMessage());
            holder.user.setText(messagesList.get(position).getUserLogin());
    
        }
    
        @NonNull
        @Override
        public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
            return new ChatHolder(this,view);
        }
    
    
        static class ChatHolder extends RecyclerView.ViewHolder {
    
            @BindView(R.id.user_message)
            TextView message;
            @BindView(R.id.user_login)
            TextView user;
    
            private ChatHolder(ChatMessagesAdapter chatMessagesAdapter, View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
    
            }
        }
    
    }
    

    And it works perfectly fine. I've based that on this example: https://www.programcreek.com/java-api-examples/?code=panzerama/Dispatch/Dispatch-master/app/src/main/java/com/indexyear/jd/dispatch/activities/DispatchTeamActivity.java

    So if you would have any problem with whole FirebaseUI thing, the above is proper example in my opinion.

    Thanks to @Alex Malmo and @Yupi for suggestions!