/
FirestoreChatActivity.java
168 lines (139 loc) · 6.1 KB
/
FirestoreChatActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package com.firebase.uidemo.database.firestore;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.firebase.ui.auth.util.ui.ImeHelper;
import com.firebase.ui.firestore.FirestoreRecyclerAdapter;
import com.firebase.ui.firestore.FirestoreRecyclerOptions;
import com.firebase.uidemo.R;
import com.firebase.uidemo.database.ChatHolder;
import com.firebase.uidemo.databinding.ActivityChatBinding;
import com.firebase.uidemo.util.SignInResultNotifier;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.CollectionReference;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.Query;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Class demonstrating how to setup a {@link RecyclerView} with an adapter while taking sign-in
* states into consideration. Also demonstrates adding data to a ref and then reading it back using
* the {@link FirestoreRecyclerAdapter} to build a simple chat app.
* <p>
* For a general intro to the RecyclerView, see <a href="https://developer.android.com/training/material/lists-cards.html">Creating
* Lists</a>.
*/
@SuppressLint("RestrictedApi")
public class FirestoreChatActivity extends AppCompatActivity
implements FirebaseAuth.AuthStateListener {
private static final String TAG = "FirestoreChatActivity";
private static final CollectionReference sChatCollection =
FirebaseFirestore.getInstance().collection("chats");
/** Get the last 50 chat messages ordered by timestamp . */
private static final Query sChatQuery =
sChatCollection.orderBy("timestamp", Query.Direction.DESCENDING).limit(50);
static {
FirebaseFirestore.setLoggingEnabled(true);
}
private ActivityChatBinding mBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = ActivityChatBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setReverseLayout(true);
manager.setStackFromEnd(true);
mBinding.messagesList.setHasFixedSize(true);
mBinding.messagesList.setLayoutManager(manager);
mBinding.messagesList.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
if (bottom < oldBottom) {
mBinding.messagesList.postDelayed(() -> mBinding.messagesList.smoothScrollToPosition(
0), 100);
}
});
ImeHelper.setImeOnDoneListener(mBinding.messageEdit, () -> onSendClick());
mBinding.sendButton.setOnClickListener(view -> onSendClick());
}
@Override
public void onStart() {
super.onStart();
if (isSignedIn()) {
attachRecyclerViewAdapter();
}
FirebaseAuth.getInstance().addAuthStateListener(this);
}
@Override
protected void onStop() {
super.onStop();
FirebaseAuth.getInstance().removeAuthStateListener(this);
}
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth auth) {
mBinding.sendButton.setEnabled(isSignedIn());
mBinding.messageEdit.setEnabled(isSignedIn());
if (isSignedIn()) {
attachRecyclerViewAdapter();
} else {
Toast.makeText(this, R.string.signing_in, Toast.LENGTH_SHORT).show();
auth.signInAnonymously().addOnCompleteListener(new SignInResultNotifier(this));
}
}
private boolean isSignedIn() {
return FirebaseAuth.getInstance().getCurrentUser() != null;
}
private void attachRecyclerViewAdapter() {
final RecyclerView.Adapter adapter = newAdapter();
// Scroll to bottom on new messages
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
mBinding.messagesList.smoothScrollToPosition(0);
}
});
mBinding.messagesList.setAdapter(adapter);
}
public void onSendClick() {
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
String name = "User " + uid.substring(0, 6);
onAddMessage(new Chat(name, mBinding.messageEdit.getText().toString(), uid));
mBinding.messageEdit.setText("");
}
@NonNull
private RecyclerView.Adapter newAdapter() {
FirestoreRecyclerOptions<Chat> options =
new FirestoreRecyclerOptions.Builder<Chat>()
.setQuery(sChatQuery, Chat.class)
.setLifecycleOwner(this)
.build();
return new FirestoreRecyclerAdapter<Chat, ChatHolder>(options) {
@NonNull
@Override
public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ChatHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.message, parent, false));
}
@Override
protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) {
holder.bind(model);
}
@Override
public void onDataChanged() {
// If there are no chat messages, show a view that invites the user to add a message.
mBinding.emptyTextView.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
};
}
private void onAddMessage(@NonNull Chat chat) {
sChatCollection.add(chat).addOnFailureListener(this,
e -> Log.e(TAG, "Failed to write message", e));
}
}