Skip to content

Let devs intercept onChanged events and control when to update their adapter #488

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Jan 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5d5635a
Update api to let users do their own filtering + sorting
SUPERCILEX Dec 8, 2016
0e3ebbc
Cleanup
SUPERCILEX Dec 9, 2016
9d462dc
Cleanup
SUPERCILEX Dec 9, 2016
276a5ba
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Dec 16, 2016
0b01f48
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Jan 2, 2017
9ea660e
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Jan 3, 2017
0f12fe3
Add documentation
SUPERCILEX Jan 3, 2017
0fa222d
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Jan 3, 2017
2899205
Cleanup
SUPERCILEX Jan 3, 2017
0826541
Cleanup
SUPERCILEX Jan 4, 2017
bc43a01
Merge remote-tracking branch 'origin/enable-filtering+predicates' int…
SUPERCILEX Jan 4, 2017
e8a9905
Merge branch 'version-1.1.0-dev' into enable-filtering+predicates
SUPERCILEX Jan 5, 2017
ab3743e
Extract onComplete listener into static inner class to prevent activi…
SUPERCILEX Jan 5, 2017
08159bd
Cleanup
SUPERCILEX Jan 5, 2017
b066b54
Temporarily ignore robolectric updates
SUPERCILEX Jan 5, 2017
3e5b71d
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Jan 7, 2017
1c1dfb7
Address review comments
SUPERCILEX Jan 7, 2017
a9bad96
Rename `OnChangedListener` to `ChangeEventListener` and `onChanged` t…
SUPERCILEX Jan 9, 2017
f875b48
Rename onChildChanged in adapters
SUPERCILEX Jan 9, 2017
142a015
Merge remote-tracking branch 'firebase/version-1.1.0-dev' into enable…
SUPERCILEX Jan 11, 2017
c85ca14
Fix merge mistakes
SUPERCILEX Jan 11, 2017
3a46033
Merge remote-tracking branch 'firebase/master' into enable-filtering+…
SUPERCILEX Jan 13, 2017
50d3d37
Fix merge mistakes
SUPERCILEX Jan 13, 2017
17a136d
Fix more merge mistakes
SUPERCILEX Jan 13, 2017
4b74478
Merge remote-tracking branch 'firebase/master' into enable-filtering+…
SUPERCILEX Jan 17, 2017
358d50d
Cleanup
SUPERCILEX Jan 17, 2017
4496baf
Cleanup
SUPERCILEX Jan 17, 2017
dcf4b91
Merge remote-tracking branch 'origin/enable-filtering+predicates' int…
SUPERCILEX Jan 17, 2017
499d448
Merge remote-tracking branch 'firebase/version-1.2.0-dev' into enable…
SUPERCILEX Jan 21, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 31 additions & 18 deletions app/src/main/java/com/firebase/uidemo/database/ChatActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package com.firebase.uidemo.database;

import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RotateDrawable;
Expand All @@ -37,6 +38,7 @@
import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.firebase.uidemo.R;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
Expand Down Expand Up @@ -86,9 +88,9 @@ public void onClick(View v) {
Chat chat = new Chat(name, mMessageEdit.getText().toString(), uid);
mChatRef.push().setValue(chat, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError databaseError, DatabaseReference reference) {
if (databaseError != null) {
Log.e(TAG, "Failed to write message", databaseError.toException());
public void onComplete(DatabaseError error, DatabaseReference reference) {
if (error != null) {
Log.e(TAG, "Failed to write message", error.toException());
}
}
});
Expand All @@ -97,11 +99,10 @@ public void onComplete(DatabaseError databaseError, DatabaseReference reference)
}
});

mMessages = (RecyclerView) findViewById(R.id.messagesList);

mManager = new LinearLayoutManager(this);
mManager.setReverseLayout(false);

mMessages = (RecyclerView) findViewById(R.id.messagesList);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary, these are defaults.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are valid reasons for having this code here, which we could debate here. But these reasons have nothing to do with letting devs intercept change events. In future PRs please keep functional changes separate from code cleanup.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this change, but I am curious, why are we setting default values and adding seemingly unnecessary code?

mMessages.setHasFixedSize(false);
mMessages.setLayoutManager(mManager);
}
Expand Down Expand Up @@ -145,7 +146,6 @@ private void attachRecyclerViewAdapter() {
Query lastFifty = mChatRef.limitToLast(50);
mRecyclerViewAdapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(
Chat.class, R.layout.message, ChatHolder.class, lastFifty) {

@Override
public void populateViewHolder(ChatHolder chatView, Chat chat, int position) {
chatView.setName(chat.getName());
Expand Down Expand Up @@ -180,20 +180,13 @@ public void onItemRangeInserted(int positionStart, int itemCount) {
private void signInAnonymously() {
Toast.makeText(this, "Signing in...", Toast.LENGTH_SHORT).show();
mAuth.signInAnonymously()
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
.addOnSuccessListener(this, new OnSuccessListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
Log.d(TAG, "signInAnonymously:onComplete:" + task.isSuccessful());
if (task.isSuccessful()) {
Toast.makeText(ChatActivity.this, "Signed In",
Toast.LENGTH_SHORT).show();
attachRecyclerViewAdapter();
} else {
Toast.makeText(ChatActivity.this, "Sign In Failed",
Toast.LENGTH_SHORT).show();
}
public void onSuccess(AuthResult result) {
attachRecyclerViewAdapter();
}
});
})
.addOnCompleteListener(new SignInResultNotifier(this));
}

public boolean isSignedIn() {
Expand Down Expand Up @@ -297,4 +290,24 @@ public void setText(String text) {
mTextField.setText(text);
}
}

/**
* Notifies the user of sign in successes or failures beyond the lifecycle of an activity.
*/
private static class SignInResultNotifier implements OnCompleteListener<AuthResult> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition! I'd appreciate some documentation on this class though.

private Context mContext;

public SignInResultNotifier(Context context) {
mContext = context.getApplicationContext();
}

@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Toast.makeText(mContext, R.string.signed_in, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, R.string.sign_in_failed, Toast.LENGTH_SHORT).show();
}
}
}
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@

<!-- strings for Auth UI demo activities -->
<string name="start_chatting">No messages. Start chatting at the bottom!</string>
<string name="sign_in_failed">Sign In Failed</string>
<string name="signed_in">Signed In</string>
</resources>
4 changes: 2 additions & 2 deletions database/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ ref.limitToLast(5).addValueEventListener(new ValueEventListener() {
}
}
@Override
public void onCancelled(DatabaseError firebaseError) {
Log.e("Chat", "The read failed: " + firebaseError.getText());
public void onCancelled(DatabaseError error) {
Log.e("Chat", "The read failed: " + error.getText());
}
});
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ public static void runAndWaitUntil(FirebaseArray array,
Runnable task,
Callable<Boolean> done) throws InterruptedException {
final Semaphore semaphore = new Semaphore(0);
array.setOnChangedListener(new FirebaseArray.OnChangedListener() {

array.setOnChangedListener(new ChangeEventListener() {
@Override
public void onChildChanged(EventType type, int index, int oldIndex) {
public void onChildChanged(ChangeEventListener.EventType type, int index, int oldIndex) {
semaphore.release();
}

@Override
public void onDataChanged() {}

@Override
public void onCancelled(DatabaseError databaseError) {
throw new IllegalStateException(databaseError.toException());
public void onCancelled(DatabaseError error) {
throw new IllegalStateException(error.toException());
}
});
task.run();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.firebase.ui.database;

import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;

public interface ChangeEventListener {
/**
* The type of event received when a child has been updated.
*/
enum EventType {
/**
* An onChildAdded event was received.
*
* @see ChildEventListener#onChildAdded(DataSnapshot, String)
*/
ADDED,
/**
* An onChildChanged event was received.
*
* @see ChildEventListener#onChildChanged(DataSnapshot, String)
*/
CHANGED,
/**
* An onChildRemoved event was received.
*
* @see ChildEventListener#onChildRemoved(DataSnapshot)
*/
REMOVED,
/**
* An onChildMoved event was received.
*
* @see ChildEventListener#onChildMoved(DataSnapshot, String)
*/
MOVED
}

/**
* A callback for when a child has changed in FirebaseArray.
*
* @param type The type of event received
* @param index The index at which the change occurred
* @param oldIndex If {@code type} is a moved event, the previous index of the moved child.
* For any other event, {@code oldIndex} will be -1.
*/
void onChildChanged(EventType type, int index, int oldIndex);

/** This method will be triggered each time updates from the database have been completely processed.
* So the first time this method is called, the initial data has been loaded - including the case
* when no data at all is available. Each next time the method is called, a complete update (potentially
* consisting of updates to multiple child items) has been completed.
* <p>
* You would typically override this method to hide a loading indicator (after the initial load) or
* to complete a batch update to a UI element.
*/
void onDataChanged();

/**
* This method will be triggered in the event that this listener either failed at the server,
* or is removed as a result of the security and Firebase Database rules.
*
* @param error A description of the error that occurred
*/
void onCancelled(DatabaseError error);
}
31 changes: 10 additions & 21 deletions database/src/main/java/com/firebase/ui/database/FirebaseArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,8 @@
* This class implements an array-like collection on top of a Firebase location.
*/
class FirebaseArray implements ChildEventListener, ValueEventListener {
public interface OnChangedListener {
enum EventType {ADDED, CHANGED, REMOVED, MOVED}

void onChildChanged(EventType type, int index, int oldIndex);

void onDataChanged();

void onCancelled(DatabaseError databaseError);

}

private Query mQuery;
private OnChangedListener mListener;
private ChangeEventListener mListener;
private List<DataSnapshot> mSnapshots = new ArrayList<>();

public FirebaseArray(Query ref) {
Expand Down Expand Up @@ -80,21 +69,21 @@ public void onChildAdded(DataSnapshot snapshot, String previousChildKey) {
index = getIndexForKey(previousChildKey) + 1;
}
mSnapshots.add(index, snapshot);
notifyChangedListeners(OnChangedListener.EventType.ADDED, index);
notifyChangedListeners(ChangeEventListener.EventType.ADDED, index);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely a better name! +1

}

@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildKey) {
int index = getIndexForKey(snapshot.getKey());
mSnapshots.set(index, snapshot);
notifyChangedListeners(OnChangedListener.EventType.CHANGED, index);
notifyChangedListeners(ChangeEventListener.EventType.CHANGED, index);
}

@Override
public void onChildRemoved(DataSnapshot snapshot) {
int index = getIndexForKey(snapshot.getKey());
mSnapshots.remove(index);
notifyChangedListeners(OnChangedListener.EventType.REMOVED, index);
notifyChangedListeners(ChangeEventListener.EventType.REMOVED, index);
}

@Override
Expand All @@ -103,7 +92,7 @@ public void onChildMoved(DataSnapshot snapshot, String previousChildKey) {
mSnapshots.remove(oldIndex);
int newIndex = previousChildKey == null ? 0 : (getIndexForKey(previousChildKey) + 1);
mSnapshots.add(newIndex, snapshot);
notifyChangedListeners(OnChangedListener.EventType.MOVED, newIndex, oldIndex);
notifyChangedListeners(ChangeEventListener.EventType.MOVED, newIndex, oldIndex);
}

@Override
Expand All @@ -116,23 +105,23 @@ public void onCancelled(DatabaseError error) {
notifyCancelledListeners(error);
}

public void setOnChangedListener(OnChangedListener listener) {
public void setOnChangedListener(ChangeEventListener listener) {
mListener = listener;
}

protected void notifyChangedListeners(OnChangedListener.EventType type, int index) {
protected void notifyChangedListeners(ChangeEventListener.EventType type, int index) {
notifyChangedListeners(type, index, -1);
}

protected void notifyChangedListeners(OnChangedListener.EventType type, int index, int oldIndex) {
protected void notifyChangedListeners(ChangeEventListener.EventType type, int index, int oldIndex) {
if (mListener != null) {
mListener.onChildChanged(type, index, oldIndex);
}
}

protected void notifyCancelledListeners(DatabaseError databaseError) {
protected void notifyCancelledListeners(DatabaseError error) {
if (mListener != null) {
mListener.onCancelled(databaseError);
mListener.onCancelled(error);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class FirebaseIndexArray extends FirebaseArray {
private static final String TAG = FirebaseIndexArray.class.getSimpleName();

private Query mQuery;
private OnChangedListener mListener;
private ChangeEventListener mListener;
private Map<Query, ValueEventListener> mRefs = new HashMap<>();
private List<DataSnapshot> mDataSnapshots = new ArrayList<>();

Expand Down Expand Up @@ -107,7 +107,7 @@ public void onChildRemoved(DataSnapshot keySnapshot) {

if (isMatch(index, key)) {
mDataSnapshots.remove(index);
notifyChangedListeners(OnChangedListener.EventType.REMOVED, index);
notifyChangedListeners(ChangeEventListener.EventType.REMOVED, index);
}
}

Expand All @@ -124,7 +124,7 @@ public void onChildMoved(DataSnapshot keySnapshot, String previousChildKey) {
DataSnapshot snapshot = mDataSnapshots.remove(oldIndex);
int newIndex = getIndexForKey(key);
mDataSnapshots.add(newIndex, snapshot);
notifyChangedListeners(OnChangedListener.EventType.MOVED, newIndex, oldIndex);
notifyChangedListeners(ChangeEventListener.EventType.MOVED, newIndex, oldIndex);
}
}

Expand All @@ -135,7 +135,7 @@ public void onCancelled(DatabaseError error) {
}

@Override
public void setOnChangedListener(OnChangedListener listener) {
public void setOnChangedListener(ChangeEventListener listener) {
super.setOnChangedListener(listener);
mListener = listener;
}
Expand All @@ -149,15 +149,15 @@ public void onDataChange(DataSnapshot snapshot) {
if (snapshot.getValue() != null) {
if (!isMatch(index, key)) {
mDataSnapshots.add(index, snapshot);
notifyChangedListeners(OnChangedListener.EventType.ADDED, index);
notifyChangedListeners(ChangeEventListener.EventType.ADDED, index);
} else {
mDataSnapshots.set(index, snapshot);
notifyChangedListeners(OnChangedListener.EventType.CHANGED, index);
notifyChangedListeners(ChangeEventListener.EventType.CHANGED, index);
}
} else {
if (isMatch(index, key)) {
mDataSnapshots.remove(index);
notifyChangedListeners(OnChangedListener.EventType.REMOVED, index);
notifyChangedListeners(ChangeEventListener.EventType.REMOVED, index);
} else {
Log.w(TAG, "Key not found at ref: " + snapshot.getRef());
}
Expand Down
Loading