Firebase updating value for a field in a child - android

I have a Child in Firebase like this in my ANDROID project:
{
"posts" : {
"-KNnbTHJGm83zpSELWwB" : {
".priority" : "6gydj46kg0",
"branch" : "Branch",
"category" : "Categoria",
"expiration" : "24h",
"g" : "6gydj46kg0",
"l" : [ -23.5415083, -46.88642 ],
"photo" : "mPostPic_20160728192627.jpeg",
"price" : 10,
"priority" : 0,
"product" : "ipad",
"userKey" : "BNd8RP5qHoc0q7f9gn8cLjPy5Ux1",
"offerMessage": "Valido ate as 14:00 hs",
"likeCount": 0
}
}
}
I just need to read the likeCount value and increment it by 1 in specific situation.
I have read a lot of Firebase documentation and sample and most part of examples is related to push data or read data and handle value changed.
I just need to read value, increment, save value again but I am newbie in Firebase and not finding a efficient way to do that.

I have splited my Json in 2 files and now the code below is working as i expected.
public void writeUserSawPost(final String postkey, int mode){
final DatabaseReference mPostsRef = mDatabase.child("posts/" + postkey);
String user = mAuth.getCurrentUser().getUid();
final DatabaseReference mPostUser = mDatabase.child("posts-user/" + user + "/" + postkey + "/sentiment");
if (mode == Constants.LIKE) {
mPostsRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
p.likeCount = p.likeCount + 1;
mutableData.setValue(p);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
}
});
mPostUser.setValue("like");
} else if (mode == Constants.DISLIKE){
mPostUser.setValue("dislike");
}
}

Related

FirebaseUI - get random list using FirebaseRecyclerAdapter

Firebase data structure
{ "questions" : {
"-KkLGS71meu8aA_wcqTq" : {
"q_answer" : "....",
"q_category" : "....",
"q_qid" : "1",
"q_question" : "..."
},
"-KkLGkf8tNb-OKwu2qVa" : {
"q_answer" : "...",
"q_category" : "...",
"q_qid" : "2",
"q_question" : "..."
},
"-KkLH1T1ZhFKJaqYFuJ4" : {
"q_answer" : "...",
"q_category" : "...",
"q_qid" : "3",
"q_question" : "..."
} }
Database refrence
questionRef.orderByChild("q_qid")
how do i generate random query and display with FirebaseRecyclerAdapter?
Is there any other way to do it?
Edit :
I try this code
questionRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long allNum = dataSnapshot.getChildrenCount();
int maxNum = (int)allNum;
int minNum = 1;
int randomNum = new Random().nextInt(maxNum - minNum + 1) + minNum;
int count = 0;
Iterable<DataSnapshot> ds = dataSnapshot.getChildren();
Iterator<DataSnapshot> ids = ds.iterator();
Map<String, Object> question;
while(ids.hasNext() && count < randomNum) {
question = (Map<String, Object>) ids.next().getValue();
String q = question.get("q_question").toString();
viewHolder.question.setText(q);
count ++; // used as positioning.
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But some question appear more than once ?
What you need to do, is to find out the number of possible values, numVals in the database and then use that in the following line of code:
int random = new Random().nextInt(numVals);
If you are using in your code an iterator to iterate a DataSnapshot than just iterate a random number of times to get the required value.
Hope it helps.

Firebase - Group specific type of data and store as single in new table

I have below structure for a table in my application with Firebase Database
"storePurchase":{
"$user_id":{
".read": "$user_id===auth.uid",
".write":"$user_id===auth.uid",
"$storeId":{
".validate":"root.child('stores/'+auth.uid+'/'+$storeId).exists()",
"$pId":{
"cId":{
".validate":"root.child('customers/'+auth.uid+'/'+newData.val()).exists()",
},
"amount":{
},
"rate":{
},
}
}
}
},
that stores data as below:
{
"-KeyN0kMSGZbb9hb9lNm" : {
"-Kf2_5kJoLctmlGcR4d1" : {
"amount" : "2500",
"cid" : "-KdChbdoV7nxwZoGLWsY", //cust1
"rate" : "5"
},
"-Kf2_e05toGelnjRMbfg" : {
"amount" : "10000",
"cid" : "-KdCggxld52mq0DsGRjH", //cust2
"rate" : "0.5"
},
"-Kf2auWq_hQtTErp55SY" : {
"amount" : "5869",
"cid" : "-KdChbdoV7nxwZoGLWsY", //cust1
"rate" : "58"
},
"-Kf2b1QoF4I7LAVAL0_4" : {
"amount" : "25000",
"cid" : "-KdChbdoV7nxwZoGLWsY", //cust1
"rate" : "0.8"
},
"-Kf2bEwN5PtwGUec_UFh" : {
"amount" : "2500",
"cid" : "-KdCggxld52mq0DsGRjH", //cust2
"rate" : "0.8"
},
"-Kf2b_tZo3rVJbegZNXa" : {
"amount" : "5000",
"cid" : "-KdCgJKh6_Rb8MOi-fIj", //cust3
"rate" : "1.5"
}
}
}
I am having cId i.e. customer ID and now I want to combine these data of each customer and store it in a separate table as unique values. How could I combine these data? I have tried using for loop and tried storing these values on addValueEventListener into a separate list, but couldn't get succeeded? How else can I combine these data based on cId and sum up the amount?
EDIT
Am trying to achieve below data structure.
"tallyBook":{
"$user_id":{
".read": "$user_id===auth.uid",
".write":"$user_id===auth.uid",
"$storeId":{
".validate":"root.child('stores/'+auth.uid+'/'+$storeId).exists()",
"cId":{
".validate":"root.child('customers/'+auth.uid+'/'+newData.val()).exists()",
"totalRate":{
},
"totalAmt":{
},
},
}
}
}
Running this transaction every time that a purchase is placed should keep your tallybook up to date in the way that you want it. So run this transaction every time you push a purchase to Firebase.
dbRef.child("tallyBook/"+FirebaseAuth‌​.getInstance().getCu‌​rrentUser().getUid()‌+"/"+storeId+"/"+purchase1.cid).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
TallyBook tally = mutableData.getValue(TallyBook.class);
if (tally == null) {
tally = new TallyBook();
tally.totalRate = purchase1.rate;
tally.totalAmount = purchase1.amount;
} else {
tally.totalRate = String.valueOf(Double.parseDouble(tally.totalRate) + Double.parseDouble(purchase1.rate));
tally.totalAmount = String.valueOf(Double.parseDouble(tally.totalAmount) + Double.parseDouble(purchase1.amount));
}
mutableData.setValue(tally);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {}
});
}
With Purchase
public class Purchase {
public String rate;
public String cid;
public String amount;
public Purchase() {
}
}
And TallyBook
public class TallyBook {
public String totalRate;
public String totalAmount;
public TallyBook() {
}
}
Then you can use the following query to get the tallybook for a particular user and store.
dbRef.child("tallyBook/"+FirebaseAuth‌​.getInstance().getCu‌​rrentUser().getUid()‌+"/"+storeId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot tally : dataSnapshot.getChildren()) {
// I called this TallyBook but it's more like one entry in a tallybook
TallyBook tally = tally.getValue(TallyBook.class);
// Do something with tally, display it or whatever
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Android Firebase create lists based on group membership

I'm trying to create a list that only contains items of a particular group. For example, I've created/written a "fruits" node and a "groups" node myself in the database console (including the groups' keys).
Relatively new to Firebase, so still trying to wrap my ahead around flattening data structures.
The JSON (showing "fruits" and "groups") looks like the following:
{
"fruits" : {
"apple" : {
"color" : "appleColorValueHere",
"groups" : {
"GroupABCKey" : true,
"GroupXYZKey" : true
},
"origin" : "appleOriginValueHere",
"size" : "appleSizeValueHere"
},
"orange" : {
"color" : "orangeColorValueHere",
"groups" : {
"GroupABCKey" : true,
"GroupXYZ" : true
},
"origin" : "orangeOriginValueHere",
"size" : "orangeSizeValueHere"
},
"strawberry" : {
"color" : "strawberryColorValueHere",
"groups" : {
"GroupJKLKey" : true
},
"origin" : "strawberryOriginValueHere",
"size" : "strawberrySizeValueHere"
}
},
"groups" : {
"GroupABCKey" : {
"members" : {
"apple" : true,
"orange" : true
},
"name" : "Group ABC Name Here"
},
"GroupJKLKey" : {
"members" : {
"strawberry" : true
},
"name" : "Group JKL Name Here"
},
"GroupXYZKey" : {
"members" : {
"apple" : true,
"orange" : true
},
"name" : "Group XYZ Name Here"
}
} ...
...
}
Within the app itself, a user can create a list which copies the entire fruits node into their newly created list (see JSON further down for user-list-items). I'm then displaying the items using FirebaseRecyclerAdapter
Here is the code for how I'm doing that:
private void writeNewFruitList(String userId, String username, String email, String title) {
final String key = databaseReference.child("fruit-lists").push().getKey();
UserLists userLists = new UserLists(userId, username, email, title);
HashMap<String, Object> updatedListToAddMap = new HashMap<>();
HashMap<String, Object> itemToAdd =
(HashMap<String, Object>) new ObjectMapper().convertValue(userLists, Map.class);
updatedListToAddMap.put("/fruit-lists/" + key, itemToAdd);
updatedListToAddMap.put("/user-fruit-lists/" + userId + "/" + key, itemToAdd);
databaseReference.updateChildren(updatedListToAddMap);
final DatabaseReference subDatabaseReference = FirebaseDatabase.getInstance().getReference();
subDatabaseReference.child("fruits").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
Log.d(TAG, child.getKey());
Log.d("fruitValues::", dataSnapshot.getValue().toString());
final String fruitKey= child.getKey();
FruitModel fruitModel = child.getValue(FruitModel.class);
Map<String, Object> nodeValues = fruitModel.toMap();
Map<String, Object> childUpdates = new HashMap<>();
String new_path = "/user-list-items/" + key + "/" + fruitKey+ "/";
childUpdates.put(new_path, nodeValues);
subDatabaseReference.updateChildren(childUpdates);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "onCancelled", databaseError.toException());
}
});
}
Q: How can I implement this process using groups? E.g., a user creates a list and only wants the newly created list to contain fruits that are of GroupABCKey but they also want the details of those fruits (size, origin etc).
Q2: What's the best way to edit my FruitModel.java to account for the groups?
public class FruitModel {
public String size;
public String origin;
public String color;
public FruitModel() {
}
public FruitModel(String size, String origin, String color) {
this.size = size;
this.origin = origin;
this.color = color;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("size", size);
result.put("origin", origin);
result.put("color", color);
return result;
}
// getters & setters
}
Finally, here's what I believe the user-list-items node would look like if done correctly?
listKey1forUserA is what I am creating correctly (except for mapping the groups as mentioned previously).
listKey2forUserA is what I'm trying to accomplish - A user wants to create and populate a list only with items that are in "GroupJKLKey"
"user-list-items" : {
"listKey1forUserA" : {
"apple" : {
"color" : "appleColorValueHere",
"groups" : {
"GroupABCKey" : true,
"GroupXYZ" : true
},
"origin" : "appleOriginValueHere",
"size" : "appleSizeValueHere"
},
"orange" : {
"color" : "orangeColorValueHere",
"groups" : {
"GroupABCKey" : true,
"GroupXYZKey" : true
},
"origin" : "orangeOriginValueHere",
"size" : "orangeSizeValueHere"
},
"strawberry" : {
"color" : "strawberryColorValueHere",
"groups" : {
"GroupJKLKey" : true
},
"origin" : "strawberryOriginValueHere",
"size" : "strawberrySizeValueHere"
}
},
"listKey2forUserA" : {
"strawberry" : {
"color" : "strawberryColorValueHere",
"groups" : {
"GroupJKLKey" : true
},
"origin" : "strawberryOriginValueHere",
"size" : "strawberrySizeValueHere"
}
}
}
Is this the proper way to go about this? Any help or a point in the right direction would be great.
Although my usage has slightly changed since I originally posted this question, the two use cases below are still applicable with some minor adjusting. There's probably a better way to go about this, so I'd definitely welcome any suggestions or ideas from others.
The tl;dr is that you structure your database in such a way that you have a groups path and a users path. The users path contains no reference to groups. Get a datasnapshot of the children of a particular group (just need the keys) and add them to an ArrayList. > For each item in your ArrayList, plug that into the users path.
This will then allow access to the details of those users (or fruits, as used in my original question) and the ability to display that information or create new information however you see fit.
Create/Write a list of people based on group membership:
private void writeListFromGroupMembership(){
final DatabaseReference mainPathReference = FirebaseDatabase.getInstance().getReference();
final ArrayList<String> groupMembers = new ArrayList<>();
DatabaseReference groupsReference = FirebaseDatabase.getInstance().getReference()
.child("groups")
.child("someOtherChildYouMayHave")
.child("nameOfGroupYouAreInterestedIn");
/* Step 1: Get a list of people in a specific group that you are interested in */
groupsReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String groupMemberKey = child.getKey();
if (child.getValue(Boolean.class) != null) {
groupMembers.add(groupMemberKey);
} else {
// TODO
}
}
/* Step 2: Loop through your list of people and insert them as a child like the following: */
for (int i = 0; i < groupMembers.size(); i++) {
final String singleGroupMember = groupMembers.get(i);
mainPathReference.child("all-groups").child("anotherChildYouMayHave").child(singleGroupMember)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Make a POJO for the item and immediately turn it into a HashMap
Person person= dataSnapshot.getValue(Person.class);
Map<String, Object> nodeValues = person.toMap();
// Make a map for the items you are adding
Map<String, Object> childUpdates = new HashMap<>();
// The path where the items will be copied/written to.
String newPath = "/master-list-items/" + listKey + "/" + singleGroupMember + "/";
// Add the items to the update map
childUpdates.put(newPath, nodeValues);
// Do the update
mainPathReference.updateChildren(childUpdates);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled: ", databaseError.toException());
}
});
}
}
// your other onCancelled override method here
}
Display a list of people based on group membership (using a Spinner, for example) :
public List<Person> persons;
...
private void getGroupMembers(String groupSelected) {
groupRef= FirebaseDatabase.getInstance().getReference()
.child("someOtherChildThatYouHouseGroupsUnder")
.child(groupSelected)
.child("members");
groupRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String groupMember= child.getKey();
selectedGroupMembers.add(groupMember);
}
for (int i = 0; i < selectedGroupMembers.size(); i++) {
String singleMember = selectedGroupMembers.get(i);
singleMemberDatabaseReference = FirebaseDatabase.getInstance().getReference()
.child("all-people")
.child(singleMember);
singleMemberDatabaseReference .addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Person person = dataSnapshot.getValue(Person.class);
persons.add(person);
personAdapter = new PersonAdapter(getApplicationContext(), persons);
recyclerView.setAdapter(personAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG,"onCancelled: ", databaseError.toException());
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled: ", databaseError.toException());
}
});
}
For displaying a list of people based on group membership, my database structure is similar to:
{
"all-groups":{
"groupNameorKeyOfGroupName":{
"details":{
"meetingLocation":"110 William Street",
"someOtherChild":"Your value"
},
"members":{
"fakeUserA":true,
"fakeUserB":true,
"fakeUserC":true,
"fakeUserD":true
}
}
}
"all-users":{
"fakeUserA":{
},
"fakeUserB":{
}
}
}

Android Firebase wait for data

in my android application I create an activity which contains a ListView which is populated with data from Firebase Database.
The JSON Tree of the structure of the database is the following:
{
"companies" : {
"companyX" : {
"address" : "50th avenue, NY",
"name" : "Spare-Tools Ltd."
},
"companyZ" : {
"address" : "50th Broadway, NY",
"name" : "Burgers and Burgers"
}
},
"company-requests" : {
"companyX" : {
"req1" : true
"req2" : true
}
},
"requests" : {
"req1" : {
"destination" : "Upper Tooting 122, Bronx",
"origin" : "Philadelphia",
"time" : "1473593287",
...
}
"req2" : {
...
}
}
}
I want to populate the ListView with the list of requests from the requests node. But I first need to know all requests that belong to a specific company so I first go to the company-requests node and retrieve all the request-keys belonging to the specific company.
The problem I am facing is that the ListView is created before the final data from the database arrived:
public class RequestsListActivity extends AppCompatActivity {
private ListView rListView;
DatabaseReference rootNode = FirebaseDatabase.getInstance().getReference();
#Override
protected void onCreate(Bundle savedInstanceState) {
...
rListView = (ListView) findViewById(R.id.result_list_view);
//First I retrieve all the requests of a specific company
DatabaseReference companyRequests = rootNode.child("company-requests/companyX");
companyRequests.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Then I retrieve all the keys of these requests
...
while (iterator.hasNext()) {
String key = iterator.next().getKey();
//For each key I retrieve its details from the requests node
DatabaseReference currRequest = rootNode.child("requests/" + key);
currRequest.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String time;
time = (String) dataSnapshot.child("time").getValue();
Request request = new Request(time);
allRequests.add(request);
}
...onCancelled...
});
}
//THIS CODE IS EXECUTED TO EARLY: BEFORE WE HAVE ANY DATA FROM FIREBASE
RequestAdapter adapter = new RequestAdapter(RequestsListActivity.this, allRequests);
rListView.setAdapter(adapter);
}
...onCancelled...
});
}
}
How can I insert a wait (spinner?) that waits until the values are loaded from Firebase?
You can use a simple counter to keep track of the number of pending loads:
companyRequests.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
// at the start we need to still load all children
final long[] pendingLoadCount = { dataSnapshot.getChildrenCount() };
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
//For each key I retrieve its details from the requests node
DatabaseReference currRequest = rootNode.child("requests/" + childSnapshot.getKey());
currRequest.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
String time;
time = (String) dataSnapshot.child("time").getValue();
Request request = new Request(time);
allRequests.add(request);
// we loaded a child, check if we're done
pendingLoadCount[0] = pendingLoadCount[0] - 1;
if (pendingLoadCount[0] == 0) {
RequestAdapter adapter = new RequestAdapter(RequestsListActivity.this, allRequests);
rListView.setAdapter(adapter);
}
}
...onCancelled...
});
}
}
});
I solved this using a java.util.concurrent.CountDownLatch:
In this example, replace EquityTotalListener with your implementation of ValueEventListener.
private void recalculate() {
final AtomicLong sumUpAll = new AtomicLong();
final CountDownLatch cnt = new CountDownLatch(mapUid2GeoLocation.keySet().size());
for (final String uid : mapUid2GeoLocation.keySet()) {
EquityTotalListener el = mapUid2EquityListener.get(uid);
if (el != null) {
if (logger.isDebugEnabled()) {
logger.debug("Listener for " + uid + " already set up");
cnt.countDown();
}
} else {
el = new EquityTotalListener(database.getDatabase(), uid) {
#Override
public void onCancelled(final DatabaseError databaseError) {
super.onCancelled(databaseError);
cnt.countDown();
}
#Override
protected void valueChanged(final String key, final Object value) {
if (value != null) {
sumUpAll.getAndAdd(Long.parseLong(value.toString()));
cnt.countDown();
}
};
}.attach();
mapUid2EquityListener.put(uid, el);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Waitung for countdown..");
}
try {
final boolean allGood = cnt.await(10, TimeUnit.SECONDS);
if (allGood) {
if (logger.isDebugEnabled()) {
logger.debug("Done waiting, " + uid + " owns " + sumUpAll.get() + " equity");
}
} else {
if (logger.isWarnEnabled()) {
logger.warn("Waiting for read operations ran into timeout");
}
}
} catch (final InterruptedException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getLocalizedMessage(), e);
}
}
}

Update ArrayAdapter with Firebase ChildEventListener()

I have a Firebase db structured like so:
{
"reviews" : {
"Star Trek Into Darkness" : {
"j" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
},
"Star Wars" : {
"badabing" : {
"major" : "Computer Science",
"rating" : 3.5,
"username" : "badabing"
},
"j" : {
"major" : "NONE",
"rating" : 5.0,
"username" : "j"
}
},
"Star Wars: The Clone Wars" : {
"j" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
},
"Star Wars: The Force Awakens" : {
"badabing" : {
"major" : "Computer Science",
"rating" : 5.0,
"username" : "badabing"
},
"j" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
}
}
There are movies under reviews and each movie has a username, major, and rating. I'm interested in getting those three children PER username (the parent of the three). What is supposed to be happening is when a user views the "Movie Details" screen of my app--they will see the reviews left by other users. Simple enough.
I'm having major difficulty with updating my custom ReviewAdapter. Currently, if we click on a movie that doesn't have a review--I can add them no problem. I can even submit an edit to the review I left. The only issue is that the submitted review submits two reviews--so I end up with a duplicate until I leave the activity and come back.
My issue can be seen in this recording here. (No sound)
I suspect it has something to do with the way I'm using Firebase's childEventListener(), but I couldn't figure out what to change after spending 4hrs+ on this issue alone.
MovieDetailActivity.java
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_movie_detail);
ButterKnife.bind(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
if (bundle != null) {
mMovieTitle = (String) bundle.get("title");
mTempMovieTitle.setText(mMovieTitle);
}
setupReviews(); //calling it here
}
public void leaveReview() {
//get current username
final String currentUser = SessionManager.getInstance(MovieDetailActivity.this).getLoggedInUsername();
final MaterialDialog reviewDialog = new MaterialDialog.Builder(MovieDetailActivity.this)
.title(leaveReviewTitle)
.customView(R.layout.rating_movie_dialog, true)
.theme(Theme.DARK)
.positiveText(save)
.negativeText(cancel)
.onPositive(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(#NonNull MaterialDialog reviewDialog, #NonNull DialogAction which) {
final RatingBar ratingBar = ButterKnife.findById(reviewDialog, R.id.rating_bar);
final double rating = ratingBar.getRating(); //get the rating
/*Get Major from Firebase, and also store the review while we're at it*/
mUserRef.child(currentUser).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String major = dataSnapshot.child("major").getValue(String.class);
final Firebase reviewRef = mReviewRef.child(StringHelper.reviewHelper(mMovieTitle, currentUser));
reviewRef.child("username").setValue(currentUser);
reviewRef.child("major").setValue(major);
reviewRef.child("rating").setValue(rating);
setupReviews(); //and here
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}).build();
//Leave review as {current_username}
TextView reviewee = ButterKnife.findById(reviewDialog, R.id.reviewee);
reviewee.append(" " + (Html.fromHtml("<b>" + currentUser + "</b>"))); //bold the username text
reviewDialog.show();
}
/**
* *ATTEMPTS* to add and update the reviews list per each movie. It's hacky and I hate it.
*/
private void setupReviews() {
mReviewRef.child(mMovieTitle).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildKey) {
ArrayList<String> usernames = new ArrayList<>();
ArrayList<String> majors = new ArrayList<>();
ArrayList<Double> ratings = new ArrayList<>();
ArrayList<Review> reviews = new ArrayList<>();
//iterate through all of the reviews for the movie
for (DataSnapshot child : dataSnapshot.getChildren()) {
switch(child.getKey()) {
case ("username"):
usernames.add(child.getValue(String.class));
break;
case ("major"):
majors.add(child.getValue(String.class));
break;
case ("rating"):
ratings.add(child.getValue(Double.class));
break;
}
}
if (!usernames.isEmpty()) { //only want to iterate if we're rating a movie that already has reviews
for (int i = 0; i < usernames.size(); ++i) {
try {
reviews.add(new Review(usernames.get(i), majors.get(i), ratings.get(i)));
} catch (IndexOutOfBoundsException ioobe) {
}
}
}
if (mReviewAdapter == null) {
mReviewAdapter = new ReviewAdapter(MovieDetailActivity.this,
R.layout.review_list_item, reviews);
mMovieReviewsList.setAdapter(mReviewAdapter);
} else {
try {
mReviewAdapter.addAll(reviews);
mMovieReviewsList.setAdapter(mReviewAdapter);
mReviewAdapter.notifyDataSetChanged();
} catch (NullPointerException npe) {
}
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildKey) {
ArrayList<String> usernames = new ArrayList<>();
ArrayList<String> majors = new ArrayList<>();
ArrayList<Double> ratings = new ArrayList<>();
ArrayList<Review> reviews = new ArrayList<>();
//iterate through all of the reviews for the movie
for (DataSnapshot child : dataSnapshot.getChildren()) {
switch(child.getKey()) {
case ("username"):
usernames.add(child.getValue(String.class));
System.out.println("username " + child.getValue(String.class));
break;
case ("major"):
majors.add(child.getValue(String.class));
System.out.println("major " + child.getValue(String.class));
break;
case ("rating"):
ratings.add(child.getValue(Double.class));
break;
}
}
if (mReviewAdapter != null) {
mReviewAdapter.clear();
}
if (!usernames.isEmpty()) {
for (int i = 0; i < usernames.size(); ++i) {
try { //I hate that checking if Usernames != empty isn't enough, and this is
//the only way I could get it to work...
reviews.add(new Review(usernames.get(i), majors.get(i), ratings.get(i)));
} catch (IndexOutOfBoundsException ioobe) {
}
}
}
if (mReviewAdapter == null) {
mReviewAdapter = new ReviewAdapter(MovieDetailActivity.this,
R.layout.review_list_item, reviews);
mMovieReviewsList.setAdapter(mReviewAdapter);
} else {
try {
mReviewAdapter.addAll(reviews); //this might break it
mMovieReviewsList.setAdapter(mReviewAdapter);
mReviewAdapter.notifyDataSetChanged();
} catch (NullPointerException npe) {
}
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildKey) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
You would need to use Push method for this to have their own Unique Key. You are using a key with the Username of the logged user. You need to do something like this.
{
"reviews" : {
"Star Trek Into Darkness" : {
"-K3dksosdfify" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
},
"Star Wars" : {
"-K3dksosdfify2" : {
"major" : "Computer Science",
"rating" : 3.5,
"username" : "badabing"
},
"-K3dksosdfify4" : {
"major" : "NONE",
"rating" : 5.0,
"username" : "j"
}
},
"Star Wars: The Clone Wars" : {
"-K3dksosdfify10" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
},
"Star Wars: The Force Awakens" : {
"-K3dksosdfify11" : {
"major" : "Computer Science",
"rating" : 5.0,
"username" : "badabing"
},
"-K3dksosdfify123" : {
"major" : "NONE",
"rating" : 3.0,
"username" : "j"
}
}
}
The Dashes is FireBase Generated. Check the push method in the Official Document
Now everytime you use push. It will generate a Unique key so The property of the other Rating will not be disturbed.
so something like this.
Firebase ref = new Firebase(yourURLtoReviews);
ref.child(SELECTEDMOVIES);
Map<String, String> post1 = new HashMap<String, String>();
post1.put("major", "gracehop");
post1.put("rating", "3.0");
post1.put("username", "Hwoarang");
ref.push().setValue(post1);
Now you don't need to care about the keys anymore. All you have to do is
ref.onChildAdded blah blah blah, itterate it with for loop and you're done.

Categories

Resources