2
2
3
3
import android .os .Bundle ;
4
4
import android .support .annotation .NonNull ;
5
- import android .support .v7 .app .AppCompatActivity ;
6
5
import android .support .v7 .widget .LinearLayoutManager ;
7
6
import android .support .v7 .widget .RecyclerView ;
8
7
import android .util .Log ;
14
13
import android .widget .TextView ;
15
14
import android .widget .Toast ;
16
15
16
+ import com .firebase .ui .auth .ui .ImeHelper ;
17
17
import com .firebase .ui .firestore .FirestoreRecyclerAdapter ;
18
18
import com .firebase .ui .firestore .FirestoreRecyclerOptions ;
19
19
import com .firebase .uidemo .R ;
20
20
import 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 ;
22
24
import com .google .android .gms .tasks .OnFailureListener ;
23
- import com .google .android .gms .tasks .Task ;
24
25
import com .google .firebase .auth .FirebaseAuth ;
25
- import com .google .firebase .firestore .DocumentReference ;
26
+ import com .google .firebase .firestore .CollectionReference ;
26
27
import com .google .firebase .firestore .FirebaseFirestore ;
27
28
import com .google .firebase .firestore .Query ;
28
29
31
32
import butterknife .OnClick ;
32
33
33
34
/**
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>.
35
41
*/
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" ;
37
45
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
+ }
39
54
40
55
@ BindView (R .id .messagesList )
41
- RecyclerView mRecycler ;
56
+ RecyclerView mRecyclerView ;
42
57
43
58
@ BindView (R .id .sendButton )
44
59
Button mSendButton ;
@@ -49,117 +64,110 @@ public class FirestoreChatActivity extends AppCompatActivity implements Firebase
49
64
@ BindView (R .id .emptyTextView )
50
65
TextView mEmptyListMessage ;
51
66
52
- private FirebaseAuth mAuth ;
53
- private FirebaseFirestore mFirestore ;
54
- private FirestoreRecyclerAdapter <Chat , ChatHolder > mAdapter ;
55
-
56
67
@ Override
57
68
protected void onCreate (Bundle savedInstanceState ) {
58
69
super .onCreate (savedInstanceState );
59
70
setContentView (R .layout .activity_chat );
60
71
ButterKnife .bind (this );
61
72
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 ));
72
75
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 () {
78
77
@ 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 ();
89
80
}
81
+ });
82
+ }
90
83
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
+ }
97
90
98
- mRecycler .setLayoutManager (manager );
99
- mRecycler .setAdapter (mAdapter );
91
+ @ Override
92
+ protected void onStop () {
93
+ super .onStop ();
94
+ FirebaseAuth .getInstance ().removeAuthStateListener (this );
100
95
}
101
96
102
97
@ Override
103
98
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 ();
107
104
} 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 ) );
110
107
}
111
108
}
112
109
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 ;
119
112
}
120
113
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 ();
128
116
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
+ });
133
124
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 );
143
126
}
144
127
145
128
@ OnClick (R .id .sendButton )
146
129
public void onSendClick () {
147
- String uid = mAuth .getCurrentUser ().getUid ();
130
+ String uid = FirebaseAuth . getInstance () .getCurrentUser ().getUid ();
148
131
String name = "User " + uid .substring (0 , 6 );
149
132
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 ));
162
134
163
135
mMessageEdit .setText ("" );
164
136
}
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
+ }
165
173
}
0 commit comments