22
33import android .os .Bundle ;
44import android .support .annotation .NonNull ;
5- import android .support .v7 .app .AppCompatActivity ;
65import android .support .v7 .widget .LinearLayoutManager ;
76import android .support .v7 .widget .RecyclerView ;
87import android .util .Log ;
1413import android .widget .TextView ;
1514import android .widget .Toast ;
1615
16+ import com .firebase .ui .auth .ui .ImeHelper ;
1717import com .firebase .ui .firestore .FirestoreRecyclerAdapter ;
1818import com .firebase .ui .firestore .FirestoreRecyclerOptions ;
1919import com .firebase .uidemo .R ;
2020import com .firebase .uidemo .database .ChatHolder ;
21- import com .google .android .gms .tasks .OnCompleteListener ;
21+ import com .firebase .uidemo .database .realtime .Chat ;
22+ import com .firebase .uidemo .util .LifecycleActivity ;
23+ import com .firebase .uidemo .util .SignInResultNotifier ;
2224import com .google .android .gms .tasks .OnFailureListener ;
23- import com .google .android .gms .tasks .Task ;
2425import com .google .firebase .auth .FirebaseAuth ;
25- import com .google .firebase .firestore .DocumentReference ;
26+ import com .google .firebase .firestore .CollectionReference ;
2627import com .google .firebase .firestore .FirebaseFirestore ;
2728import com .google .firebase .firestore .Query ;
2829
3132import butterknife .OnClick ;
3233
3334/**
34- * Class demonstrating a simple real-time chat app that relies on {@link FirestoreRecyclerAdapter}.
35+ * Class demonstrating how to setup a {@link RecyclerView} with an adapter while taking sign-in
36+ * states into consideration. Also demonstrates adding data to a ref and then reading it back using
37+ * the {@link FirestoreRecyclerAdapter} to build a simple chat app.
38+ * <p>
39+ * For a general intro to the RecyclerView, see <a href="https://developer.android.com/training/material/lists-cards.html">Creating
40+ * Lists</a>.
3541 */
36- public class FirestoreChatActivity extends AppCompatActivity implements FirebaseAuth .AuthStateListener {
42+ public class FirestoreChatActivity extends LifecycleActivity
43+ implements FirebaseAuth .AuthStateListener {
44+ private static final String TAG = "FirestoreChatActivity" ;
3745
38- private static final String TAG = "FirestoreChat" ;
46+ private static final CollectionReference sChatCollection =
47+ FirebaseFirestore .getInstance ().collection ("chats" );
48+ /** Get the last 50 chat messages ordered by timestamp . */
49+ private static final Query sChatQuery = sChatCollection .orderBy ("timestamp" ).limit (50 );
50+
51+ static {
52+ FirebaseFirestore .setLoggingEnabled (true );
53+ }
3954
4055 @ BindView (R .id .messagesList )
41- RecyclerView mRecycler ;
56+ RecyclerView mRecyclerView ;
4257
4358 @ BindView (R .id .sendButton )
4459 Button mSendButton ;
@@ -49,117 +64,110 @@ public class FirestoreChatActivity extends AppCompatActivity implements Firebase
4964 @ BindView (R .id .emptyTextView )
5065 TextView mEmptyListMessage ;
5166
52- private FirebaseAuth mAuth ;
53- private FirebaseFirestore mFirestore ;
54- private FirestoreRecyclerAdapter <Chat , ChatHolder > mAdapter ;
55-
5667 @ Override
5768 protected void onCreate (Bundle savedInstanceState ) {
5869 super .onCreate (savedInstanceState );
5970 setContentView (R .layout .activity_chat );
6071 ButterKnife .bind (this );
6172
62- // Enable verbose Firestore logging
63- FirebaseFirestore .setLoggingEnabled (true );
64-
65- mAuth = FirebaseAuth .getInstance ();
66- mFirestore = FirebaseFirestore .getInstance ();
67-
68- // Get the last 50 chat messages, ordered by timestamp
69- Query query = mFirestore .collection ("chats" ).orderBy ("timestamp" ).limit (50 );
70-
71- LinearLayoutManager manager = new LinearLayoutManager (this );
73+ mRecyclerView .setHasFixedSize (true );
74+ mRecyclerView .setLayoutManager (new LinearLayoutManager (this ));
7275
73- FirestoreRecyclerOptions <Chat > options = new FirestoreRecyclerOptions .Builder <Chat >()
74- .setQuery (query , Chat .class )
75- .build ();
76-
77- mAdapter = new FirestoreRecyclerAdapter <Chat , ChatHolder >(options ) {
76+ ImeHelper .setImeOnDoneListener (mMessageEdit , new ImeHelper .DonePressedListener () {
7877 @ Override
79- public void onBindViewHolder (ChatHolder holder , int position , Chat model ) {
80- holder .bind (model );
81- }
82-
83- @ Override
84- public ChatHolder onCreateViewHolder (ViewGroup group , int i ) {
85- View view = LayoutInflater .from (group .getContext ())
86- .inflate (R .layout .message , group , false );
87-
88- return new ChatHolder (view );
78+ public void onDonePressed () {
79+ onSendClick ();
8980 }
81+ });
82+ }
9083
91- @ Override
92- public void onDataChanged () {
93- // If there are no chat messages, show a view that invites the user to add a message.
94- mEmptyListMessage . setVisibility ( getItemCount () == 0 ? View . VISIBLE : View . GONE );
95- }
96- };
84+ @ Override
85+ public void onStart () {
86+ super . onStart ();
87+ if ( isSignedIn ()) { attachRecyclerViewAdapter (); }
88+ FirebaseAuth . getInstance (). addAuthStateListener ( this );
89+ }
9790
98- mRecycler .setLayoutManager (manager );
99- mRecycler .setAdapter (mAdapter );
91+ @ Override
92+ protected void onStop () {
93+ super .onStop ();
94+ FirebaseAuth .getInstance ().removeAuthStateListener (this );
10095 }
10196
10297 @ Override
10398 public void onAuthStateChanged (@ NonNull FirebaseAuth auth ) {
104- if (auth .getCurrentUser () != null ) {
105- mAdapter .startListening ();
106- mSendButton .setEnabled (true );
99+ mSendButton .setEnabled (isSignedIn ());
100+ mMessageEdit .setEnabled (isSignedIn ());
101+
102+ if (isSignedIn ()) {
103+ attachRecyclerViewAdapter ();
107104 } else {
108- mAdapter . stopListening ();
109- mSendButton . setEnabled ( false );
105+ Toast . makeText ( this , R . string . signing_in , Toast . LENGTH_SHORT ). show ();
106+ auth . signInAnonymously (). addOnCompleteListener ( new SignInResultNotifier ( this ) );
110107 }
111108 }
112109
113- @ Override
114- protected void onStart () {
115- super .onStart ();
116-
117- signInAnonymously ();
118- mAuth .addAuthStateListener (this );
110+ private boolean isSignedIn () {
111+ return FirebaseAuth .getInstance ().getCurrentUser () != null ;
119112 }
120113
121- @ Override
122- protected void onStop () {
123- super .onStop ();
124-
125- mAuth .removeAuthStateListener (this );
126- mAdapter .stopListening ();
127- }
114+ private void attachRecyclerViewAdapter () {
115+ final RecyclerView .Adapter adapter = newAdapter ();
128116
129- private void signInAnonymously () {
130- if (mAuth .getCurrentUser () != null ) {
131- return ;
132- }
117+ // Scroll to bottom on new messages
118+ adapter .registerAdapterDataObserver (new RecyclerView .AdapterDataObserver () {
119+ @ Override
120+ public void onItemRangeInserted (int positionStart , int itemCount ) {
121+ mRecyclerView .smoothScrollToPosition (adapter .getItemCount ());
122+ }
123+ });
133124
134- mAuth .signInAnonymously ()
135- .addOnFailureListener (this , new OnFailureListener () {
136- @ Override
137- public void onFailure (@ NonNull Exception e ) {
138- Log .w (TAG , "signIn:failure" , e );
139- Toast .makeText (FirestoreChatActivity .this ,
140- "Authentication failed." , Toast .LENGTH_LONG ).show ();
141- }
142- });
125+ mRecyclerView .setAdapter (adapter );
143126 }
144127
145128 @ OnClick (R .id .sendButton )
146129 public void onSendClick () {
147- String uid = mAuth .getCurrentUser ().getUid ();
130+ String uid = FirebaseAuth . getInstance () .getCurrentUser ().getUid ();
148131 String name = "User " + uid .substring (0 , 6 );
149132
150- Chat chat = new Chat (name , mMessageEdit .getText ().toString (), uid );
151-
152- mFirestore .collection ("chats" ).add (chat )
153- .addOnCompleteListener (this ,
154- new OnCompleteListener <DocumentReference >() {
155- @ Override
156- public void onComplete (@ NonNull Task <DocumentReference > task ) {
157- if (!task .isSuccessful ()) {
158- Log .e (TAG , "Failed to write message" , task .getException ());
159- }
160- }
161- });
133+ onAddMessage (new Chat (name , mMessageEdit .getText ().toString (), uid ));
162134
163135 mMessageEdit .setText ("" );
164136 }
137+
138+ protected RecyclerView .Adapter newAdapter () {
139+ FirestoreRecyclerOptions <Chat > options =
140+ new FirestoreRecyclerOptions .Builder <Chat >()
141+ .setQuery (sChatQuery , Chat .class )
142+ .setLifecycleOwner (this )
143+ .build ();
144+
145+ return new FirestoreRecyclerAdapter <Chat , ChatHolder >(options ) {
146+ @ Override
147+ public ChatHolder onCreateViewHolder (ViewGroup parent , int viewType ) {
148+ return new ChatHolder (LayoutInflater .from (parent .getContext ())
149+ .inflate (R .layout .message , parent , false ));
150+ }
151+
152+ @ Override
153+ protected void onBindViewHolder (ChatHolder holder , int position , Chat model ) {
154+ holder .bind (model );
155+ }
156+
157+ @ Override
158+ public void onDataChanged () {
159+ // If there are no chat messages, show a view that invites the user to add a message.
160+ mEmptyListMessage .setVisibility (getItemCount () == 0 ? View .VISIBLE : View .GONE );
161+ }
162+ };
163+ }
164+
165+ protected void onAddMessage (Chat chat ) {
166+ sChatCollection .add (chat ).addOnFailureListener (this , new OnFailureListener () {
167+ @ Override
168+ public void onFailure (@ NonNull Exception e ) {
169+ Log .e (TAG , "Failed to write message" , e );
170+ }
171+ });
172+ }
165173}
0 commit comments