How to Get/Handle Synchronized Firestore Data Having Multiple Entries? - android

Need to Do:
Basically i want Firestore => collection "order" => docs having customerid and productid on each doc => onSuccess => add to OrderPOJOList => call getCustomerName() then getProductName() => get Names in order => add to respective ArrayList => in end combine all data from three arraylist (OrderPOJOList, CustomerName, ProductName) to CurrentOrderPOJOList => set to Adapter.
Problem:
the two listeners in getCustomerName() & getProductName() runs asynchronously and adds Name to arrayList randomly... all i want is to show data on Adapter in order but sometimes names get exchange on list due to listners running asynchronously.
What should i do to get my customer and product names from firestore in sequence to display?
public class CurrentOrders extends AppCompatActivity {
private List<CurrentOrdersPOJO> currentOrdersPOJOList;
private List<OrderPOJO> orderPOJOList;
private FirebaseFirestore firebaseFirestore;
private String DocId, Area, cname, pname;
private OrderPOJO orderPOJO;
private CurrentOrdersPOJO currentOrdersPOJO;
private int count = -1, count1 = -1, i;
private RecyclerView recyclerView;
private List<String> customerName, productName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_current_orders);
//Current User Unique ID
DocId = getIntent().getStringExtra("DocumentId");
Area = getIntent().getStringExtra("Area");
Log.w("ReachedCurrentOrders", "Doc Id: " + DocId + "\nArea: " + Area);
currentOrdersPOJOList = new ArrayList<>();
customerName = new ArrayList<String>();
productName = new ArrayList<String>();
orderPOJOList = new ArrayList<>();
recyclerView = findViewById(R.id.activity_current_order_recyclerView);
firebaseFirestore = FirebaseFirestore.getInstance();
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
firebaseFirestore.collection("order")
.whereEqualTo("area", Area)
.whereEqualTo("status", "active")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(final QuerySnapshot queryDocumentSnapshots) {
if (!queryDocumentSnapshots.isEmpty()) {
for (final QueryDocumentSnapshot queryDocumentSnapshot : queryDocumentSnapshots) {
count++;
}
for (final QueryDocumentSnapshot queryDocumentSnapshot : queryDocumentSnapshots) {
orderPOJO = queryDocumentSnapshot.toObject(OrderPOJO.class);
orderPOJOList.add(orderPOJO);
Log.d("Tagging", "The Customer UID: " + orderPOJO.getC_uid() + "\nThe Product Doc ID: " + orderPOJO.getP_docid());
count1++;
if (count == count1) {
getCustomerName();
}
}//endof for loop
} else {
Toast.makeText(CurrentOrders.this, "No Orders in Your Area", Toast.LENGTH_SHORT).show();
Log.d("CurrentOrder", "Exception Here");
}
}
});
}//endofOnCreate
public void getCustomerName() {
count1 = -1;
//Getting Customer Name from ID
for (i = 0; i <= count; i++) {
firebaseFirestore.collection("customer").document(orderPOJOList.get(i).getC_uid()).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
cname = documentSnapshot.getString("name");
customerName.add(cname);
count1++;
if (count1 == count) {
getProductName();
}
} else {
Log.d("CurrentOrders", "Exception Here" + documentSnapshot.exists());
}
}
});
}
}//end of function
public void getProductName() {
count1 = -1;
//Product Getting Name
for (i = 0; i <= count; i++) {
firebaseFirestore.collection("product").document(orderPOJOList.get(i).getP_docid()).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists()) {
pname = documentSnapshot.getString("name");
productName.add(pname);
count1++;
if (count1 == count) {
callAdapter();
}
} else {
Log.d("CurrentOrders", "Exception Here" + documentSnapshot.exists());
}
}
});
}
}//endofFunction
public void callAdapter() {
for (int i = 0; i <= count; i++) {
currentOrdersPOJO = new CurrentOrdersPOJO(customerName.get(i), orderPOJOList.get(i).getComplete_address(),
productName.get(i), orderPOJOList.get(i).getQuantity(), orderPOJOList.get(i).getStatus(), orderPOJOList.get(i).getArea(), orderPOJOList.get(i).getO_date());
currentOrdersPOJOList.add(currentOrdersPOJO);
}
recyclerView.setAdapter(new CurrentOrdersAdapter(currentOrdersPOJOList, CurrentOrders.this));
}//endofFunction
}//endofclass
[screenshot of an activity showing list that varies everytime][1]
[1]: https://i.stack.imgur.com/X48JF.jpg

A similar question has been asked on another thread, it seems that you can synchronously return data because the method itself is a task, you could try to use the Taks.await(task) method to wait for the operation to end maybe that's the answer you're looking for.

I Have solved this problem by using mentioned solution by #Ricardo above AND combining the solution with using Asynctask(Background Thread) as it was first giving IllegalStateException because of calling Tasks.await(task) on Main UI Thread.
So, Use: Tasks.await(task) on Aysnctask(Background Thread)

Related

Unable to get the image to display in recyclerview from nested realtime database

Here is my firebase realtime database table. I do have an image stored in the Firebase Storage. I'm planning to use multiple of characters in this table under the character column.
Realtime Database
I don't understand why I can't get the photo_id from the realtime database. The link (photo_id) is "https://firebasestorage.googleapis.com/v0/b/occreations-b6c14.appspot.com/o?name=characterimage%2FWk80hB04oZRMQxK9Ig0DDgoL5n52%20be58f895-8fda-4dd1-9e15-32e4186eae8a&uploadType=resumable&upload_id=AEnB2UrkBecZTlE4kpNypl-1ACjekw1CIM3UsUxXgRGXHLB4DCmyLDI-i70OxqjwKchf7sf3YURhgCFq3KxV7JUPDy55GyZWSg&upload_protocol=resumable"
When I click on this link, it says "Invalid request. X-Goog-Upload-Command header is missing."
But when I tested my code using the link from the URL into the arraylist, it works. But why I can't get the image from my database?
I can't seem to find the problem. I was thinking it could be the arraylist, the photo_id url or the way I upload to the firebase storage/realtime database maybe because I was unable to get the file name as .png or .jpeg and store it as a photo_id?
UPDATE: I change the url link to download url from Firebase Storage, but I am unable to retrieve the link from the DatabaseReference, which leaves nothing was adding into the List
I was following this tutorial.Coding-In-Flow Tutorial
UPDATE2: I have problems with nested data. I wanted to get ONLY the photo_id value (which is the url) from the realtime database, but I get it as a null value. The placeholder image is a null after I changed the recyclerview. How should I retrieve my data only from the character child to the character_id (which is already unretrievable) unless there is a way to do it. I've been searching through other answers on stackoverflow but none of them matches what I'm looking for.
My log cat says
W/ClassMapper: No setter/field for -LQq7dIvsJX7GgNfXeIf found on class com.example.linda.originalcharacterapp.model.CharacterInformation
No setter/field for -LQq7SB5qvcNXdJEzwBh found on class
com.example.linda.originalcharacterapp.model.CharacterInformation
D/TAG: null / null
W/ClassMapper: No setter/field for username found on class
com.example.linda.originalcharacterapp.model.CharacterInformation
No setter/field for email found on class
com.example.linda.originalcharacterapp.model.CharacterInformation
No setter/field for password found on class
com.example.linda.originalcharacterapp.model.CharacterInformation
D/TAG: null / null
Updated Android Screenshot
Android Screenshot
Current Image sitting in Firebase Storage
HomeFragment.class
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager (this.getActivity());
mRecyclerView.setLayoutManager(new GridLayoutManager (this.getActivity(),2));
userOCs = new ArrayList<> ();
//DatabaseReference characterReference = FirebaseDatabase.getInstance().getReference();
reference.child(userid).orderByChild("characters").getRef().addValueEventListener (new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Display images of ocs
for(DataSnapshot characterSnapshot : dataSnapshot.getChildren()){
if (dataSnapshot.exists()) {
String photoid = dataSnapshot.child ("photo_id").getValue (String.class);
String cName = dataSnapshot.child ("characterName").getValue (String.class);
CharacterInformation oc = characterSnapshot.getValue (CharacterInformation.class);
Log.d ("TAG", photoid + " / " + cName);
userOCs.add (oc);
Toast.makeText (getActivity (), "Adding images " + oc.getCharacterName (), Toast.LENGTH_SHORT).show ();
}
}
String file = "https://firebasestorage.googleapis.com/v0/b/occreations-b6c14.appspot.com/o/characterimage%2FWk80hB04oZRMQxK9Ig0DDgoL5n52%2F3aa5daa6-86b7-41e5-b6aa-2dae58a991ef.png?alt=media&token=5a4e9c2a-e1f8-4d05-904a-a1d934524a09";
userOCs.add(new CharacterInformation("12", "23",file, " Linda", "12", "Time ANGEL", "Quirk","Justin", "Flight", "lIVES IN MAIN"));
mAdapter = new RecycleViewAdapter (userOCs, getActivity()); //where the image is inserted
mRecyclerView.setAdapter(mAdapter);
}
CreateCharacter.class
private void uploadOC() {
//String values
nameValue = cName.getText ().toString ().trim ();
ageValue = cAge.getText ().toString ().trim ();
speciesValue = cSpecies.getText ().toString ().trim();
personalityValue = cPersonality.getText ().toString ().trim();
familyValue = cFamily.getText ().toString ().trim();
powerValue = cPowers.getText ().toString ().trim();
bioValue = cBiography.getText ().toString ().trim();
currentUserID = firebaseAuth.getCurrentUser().getUid();
if (!TextUtils.isEmpty (nameValue) && selectedImage != null) {
// StorageReference fileReference = storageReference.child(System.currentTimeMillis()+ "." + getFileExtension(downloadImage));
storageReference = storageReference.child("characterimage").child (currentUserID + "/" + UUID.randomUUID ().toString ());
storageReference.putFile (selectedImage)
.addOnSuccessListener (new OnSuccessListener<UploadTask.TaskSnapshot> () {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
downloadImage = taskSnapshot.getUploadSessionUri ();
String characterId = databaseReference.child("characters").push ().getKey(); //creates unique random id
// String photoName = taskSnapshot.getMetadata().getReference().getDownloadUrl().toString();
CharacterInformation newCharacter = new CharacterInformation (currentUserID, characterId, downloadImage.toString(),nameValue, ageValue, speciesValue,
personalityValue, familyValue, powerValue, bioValue);
Map<String, Object> postValue = newCharacter.toMap();
Map<String, Object> childUpdates = new HashMap<> ();
databaseReference.child("User Account").child(currentUserID).child("character").child(characterId).setValue(newCharacter);
childUpdates.put(nameValue, newCharacter);
databaseReference.updateChildren (childUpdates);
Toast.makeText (getActivity (), "Uploaded", Toast.LENGTH_SHORT).show ();
}
})
.addOnFailureListener (new OnFailureListener () {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText (getActivity (), "Failed " + e.getMessage (), Toast.LENGTH_SHORT).show ();
}
})
.addOnProgressListener (new OnProgressListener<UploadTask.TaskSnapshot> () {
#Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
double progress = (100.0 * taskSnapshot.getBytesTransferred () / taskSnapshot
.getTotalByteCount ());
}
});
}
else {
Toast.makeText (getActivity (), "Character needs name", Toast.LENGTH_SHORT).show ();
}
}
RecyclerViewAdapter.class
public class RecycleViewAdapter extends RecyclerView.Adapter<RecycleViewAdapter.ViewHolder> {
private List<CharacterInformation> mDataset;
private Context context;
public static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView image;
private Context context;
private ViewHolder(View view) {
super(view);
image = (ImageView) view.findViewById(R.id.recycleImage);
}
}
public RecycleViewAdapter(List<CharacterInformation> myDataset, Context context) {
mDataset = myDataset;
this.context = context;
}
#Override
public RecycleViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_row, parent, false);
ViewHolder vh = new ViewHolder(view);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.image.setScaleType (ImageView.ScaleType.CENTER_CROP);
CharacterInformation oc = mDataset.get(position);
Picasso.get().load(oc.getPhoto_id ())
.into(holder.image);
System.out.println("Binding images...");
Toast.makeText (context,"Binding images" + oc.getCharacterName(), Toast.LENGTH_SHORT).show();
holder.image.setOnClickListener (new View.OnClickListener () {
#Override
public void onClick(View v) {
AppCompatActivity activity = (AppCompatActivity) v.getContext();
DisplayCharacter ocFragment = new DisplayCharacter ();
activity.getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, ocFragment).addToBackStack(null).commit();
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
And I already found my answer to this question
This is how I solve my problem, but it's more like I had the no setter field problem and problems calling from the database reference.
W/ClassMapper: No setter/field for class
I had to recall the database reference to get my nested data.
public void retrieveUserOCs() {
System.out.println("Revoke the character reference ");
reference = FirebaseDatabase.getInstance().getReference("User Account");
DatabaseReference characterReference = reference.child(user.getUid()).child("character");
// final Query query = characterReference;
reference.child(user.getUid()).child("character").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
////Loop 1 to go through all the child nodes of characters
for(DataSnapshot characterSnapshot : dataSnapshot.getChildren()){
if (dataSnapshot.getValue() != null ) {
CharacterInformation oc = characterSnapshot.getValue (CharacterInformation.class);
String ocKey = characterSnapshot.getKey ();
System.out.println ("Adding ocs: " + ocKey + " Name: " + oc.getCharacterName ());
Log.d ("TAGGING OCS", ocKey + " / " + oc.getCharacterName ());
userOCs.add (oc);
Toast.makeText (getContext(), "Adding images " + oc.getCharacterName (), Toast.LENGTH_SHORT).show ();
} else { //if user haven't added any characters
break;
}
}
String file2 ="http://ghostfinder101.weebly.com/uploads/1/9/7/3/19737887/published/gear-of-diamond_1.png?1541826567";
userOCs.add(new CharacterInformation("34", "124",file2, "Diamond", "2002", "Diamond Angel", "Narcissistic","Jahara(friend)", "Flight, shard attacks", "lIVES IN MAIN"));
mAdapter = new RecycleViewAdapter (userOCs, getActivity()); //where the image is inserted
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("Database error");
}
});
}

Why is my code pulling data twice from Firebase? Android-Studio

I don't remember what I am doing wrong because I have fixed this before.
What I am trying to do is retrieve the job post data from firebase and put it in my listview but when I do that, the job posts have duplicates. Any Idea on how to fix this?
I'll add the logs and code below.
here are the logs, I keep count of the logs with a log in the script "getting jobs count first count"
05-28 20:24:06.391 17592-17592/test.com.jobTestApp D/FindWorkFragment: getPhotos: getting photos
getPhotos: getting jobs count first count = 0
05-28 20:24:06.772 17592-17592/test.com.jobTestApp D/FindWorkFragment: onDataChange: getJOBS {job_headline=Website, date_posted=yesterday, user_country=United States, time_frame= 7 days, job_desc=Hello, fullname=Tom, category=Web Development, budget=30}
onDataChange: jobs count = 1
onDataChange: getJOBS {job_headline=Website, date_posted=today, user_country=United States, user_id=QkGIMPFXDoPfpnwnPileLRtyR243, time_frame=6 days, job_desc=stuff, fullname=Odi Ohn, category=Web Development, budget=24}
onDataChange: jobs count = 2
05-28 20:24:06.773 17592-17592/test.com.jobTestApp D/FindWorkFragment: onDataChange: getJOBS {job_headline=Website, date_posted=yesterday, user_country=United States, time_frame= 7 days, job_desc=Hello, fullname=Tom, category=Web Development, budget=30}
onDataChange: jobs count = 3
onDataChange: getJOBS {job_headline=Website, date_posted=today, user_country=United States, user_id=QkGIMPFXDoPfpnwnPileLRtyR243, time_frame=6 days, job_desc=stuff, fullname=Odi Ohn, category=Web Development, budget=24}
onDataChange: jobs count = 4
05-28 20:24:06.776 17592-17592/test.com.jobTestApp D/FindWorkFragment: onDataChange: jobs count = 4
Here is the code :
public class FindWorkFragment extends Fragment implements AdapterView.OnItemSelectedListener{
private static final String TAG = "FindWorkFragment";
private ImageView btnBack;
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference myRef;
private ArrayList<PostJob> mJobs;
private ArrayList<PostJob> mPaginatedPhotos;
private ArrayList<String> mJobsinCat;
private ListView mListView;
private JobFeedListAdapter mAdapter;
private int mResults;
private Spinner mSearchCat;
private String searchCat;
private ArrayAdapter<CharSequence> mSearchCategoryAdapter;
public static FindWorkFragment newInstance(int instance) {
Bundle args = new Bundle();
args.putInt("argsInstance", instance);
FindWorkFragment thirdFragment = new FindWorkFragment();
thirdFragment.setArguments(args);
return thirdFragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_findwork, container, false);
final Fragment fragment = new FindWorkFragment();
mFirebaseDatabase = FirebaseDatabase.getInstance();
myRef = mFirebaseDatabase.getReference();
btnBack = (ImageView) view.findViewById(R.id.iv_back);
mSearchCat = (Spinner) view.findViewById(R.id.spSearchCat);
mSearchCategoryAdapter = ArrayAdapter.createFromResource(getActivity(), R.array.job_categories, android.R.layout.simple_spinner_item);
mSearchCategoryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSearchCat.setAdapter(mSearchCategoryAdapter);
mSearchCat.setOnItemSelectedListener(this);
mListView = (ListView) view.findViewById(R.id.joblistview);
mJobsinCat = new ArrayList<>();
mJobs = new ArrayList<>();
getJobsinCat();
btnBack.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.main_frag_container, new HomeDashboardFragment());
ft.commit();
}
});
return view;
}
private void getJobsinCat(){
Log.d(TAG, "getFollowing: searching for following");
String key = myRef.child("Web Development").push().getKey();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference
.child(getString(R.string.dbname_jobs))
.child("United States")
.child("Web Development");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()){
Log.d(TAG, "onDataChange: found user: " +
singleSnapshot.getKey());
mJobsinCat.add(singleSnapshot.getKey().toString());
}
//mJobsinCat.add(FirebaseAuth.getInstance().getCurrentUser().getUid());
//get the photos
getPhotos();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void getPhotos(){
Log.d(TAG, "getPhotos: getting photos");
Log.d(TAG, "getPhotos: getting jobs count first count = " + mJobs.size() );
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
for(int i = 0; i < mJobsinCat.size(); i++){
final int count = i;
Query query = reference
.child(getString(R.string.dbname_jobs))
.child("United States")
.child("Web Development")
.orderByKey();
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()){
PostJob job = new PostJob();
Log.d(TAG, "onDataChange: getJOBS " + singleSnapshot.getValue());
Map<String, Object> objectMap = (HashMap<String, Object>) singleSnapshot.getValue();
//job.setUser_country(objectMap.get("user_country").toString());
job.setJob_headline(objectMap.get("job_headline").toString());
job.setJob_desc(objectMap.get("job_desc").toString());
job.setBudget(objectMap.get("budget").toString());
job.setTime_frame(objectMap.get("time_frame").toString());
//job.setTags(objectMap.get(getString(R.string.field_tags)).toString());
//job.setPhoto_id(objectMap.get(getString(R.string.field_photo_id)).toString());
//job.setUser_id(objectMap.get(getString(R.string.field_user_id)).toString());
//job.setDate_posted(objectMap.get("date_posted").toString());
//job.setImage_path(objectMap.get(getString(R.string.field_image_path)).toString());
/*
// soon going to be the proposals
ArrayList<Comment> comments = new ArrayList<Comment>();
for (DataSnapshot dSnapshot : singleSnapshot
.child(getString(R.string.field_comments)).getChildren()){
Comment comment = new Comment();
comment.setUser_id(dSnapshot.getValue(Comment.class).getUser_id());
comment.setComment(dSnapshot.getValue(Comment.class).getComment());
comment.setDate_created(dSnapshot.getValue(Comment.class).getDate_created());
comments.add(comment);
}
photo.setComments(comments);*/
mJobs.add(job);
}
if(count >= mJobsinCat.size() -1){
//display our photos
displayPhotos();
Log.d(TAG, "onDataChange: jobs count = " + mJobs.size());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
private void displayPhotos(){
mPaginatedPhotos = new ArrayList<>();
if(mJobs != null){
try{
/*
Collections.sort(mJobs, new Comparator<PostJob>() {
#Override
public int compare(PostJob o1, PostJob o2) {
return o2.getDate_posted().compareTo(o1.getDate_posted());
}
});*/
int iterations = mJobs.size();
if(iterations > 10){
iterations = 10;
}
mResults = 10;
for(int i = 0; i < iterations; i++){
mPaginatedPhotos.add(mJobs.get(i));
}
mAdapter = new JobFeedListAdapter(getActivity(), R.layout.layout_jobfeed_listitem, mPaginatedPhotos);
mListView.setAdapter(mAdapter);
}catch (NullPointerException e){
Log.e(TAG, "displayPhotos: NullPointerException: " + e.getMessage() );
}catch (IndexOutOfBoundsException e){
Log.e(TAG, "displayPhotos: IndexOutOfBoundsException: " + e.getMessage() );
}
}
}
public void displayMorePhotos(){
Log.d(TAG, "displayMorePhotos: displaying more photos");
try{
if(mJobs.size() > mResults && mJobs.size() > 0){
int iterations;
if(mJobs.size() > (mResults + 10)){
Log.d(TAG, "displayMorePhotos: there are greater than 10 more photos");
iterations = 10;
}else{
Log.d(TAG, "displayMorePhotos: there is less than 10 more photos");
iterations = mJobs.size() - mResults;
}
//add the new photos to the paginated results
for(int i = mResults; i < mResults + iterations; i++){
mPaginatedPhotos.add(mJobs.get(i));
}
mResults = mResults + iterations;
mAdapter.notifyDataSetChanged();
}
}catch (NullPointerException e){
Log.e(TAG, "displayPhotos: NullPointerException: " + e.getMessage() );
}catch (IndexOutOfBoundsException e){
Log.e(TAG, "displayPhotos: IndexOutOfBoundsException: " + e.getMessage() );
}
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
searchCat = parent.getItemAtPosition(position).toString();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
}
See the getPhotos() you have added query.addListenerForSingleValueEvent in for loop so it will executed multiple time based on mJobsinCat.size(),pull it out from loop for call it single time
private void getPhotos(){
Log.d(TAG, "getPhotos: getting photos");
Log.d(TAG, "getPhotos: getting jobs count first count = " + mJobs.size() );
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference
.child(getString(R.string.dbname_jobs))
.child("United States")
.child("Web Development")
.orderByKey();
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()){
PostJob job = new PostJob();
Log.d(TAG, "onDataChange: getJOBS " + singleSnapshot.getValue());
Map<String, Object> objectMap = (HashMap<String, Object>) singleSnapshot.getValue();
//job.setUser_country(objectMap.get("user_country").toString());
job.setJob_headline(objectMap.get("job_headline").toString());
job.setJob_desc(objectMap.get("job_desc").toString());
job.setBudget(objectMap.get("budget").toString());
job.setTime_frame(objectMap.get("time_frame").toString());
//job.setTags(objectMap.get(getString(R.string.field_tags)).toString());
//job.setPhoto_id(objectMap.get(getString(R.string.field_photo_id)).toString());
//job.setUser_id(objectMap.get(getString(R.string.field_user_id)).toString());
//job.setDate_posted(objectMap.get("date_posted").toString());
//job.setImage_path(objectMap.get(getString(R.string.field_image_path)).toString());
/*
// soon going to be the proposals
ArrayList<Comment> comments = new ArrayList<Comment>();
for (DataSnapshot dSnapshot : singleSnapshot
.child(getString(R.string.field_comments)).getChildren()){
Comment comment = new Comment();
comment.setUser_id(dSnapshot.getValue(Comment.class).getUser_id());
comment.setComment(dSnapshot.getValue(Comment.class).getComment());
comment.setDate_created(dSnapshot.getValue(Comment.class).getDate_created());
comments.add(comment);
}
photo.setComments(comments);*/
mJobs.add(job);
}
Log.d(TAG, "onDataChange: jobs count = " + mJobs.size());
if( mJobs.size()>0){
//display our photos
displayPhotos();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}

workaround for async call to read from Firebase

I'm new to Android and Firebase environment but I'm working on it !
I'm working on an Android app and I need to read some values related to a child within a Firebase database. After this initial read, I need to modify / update these values and write them to the same child.
public class MainActivity extends Activity {
public static class Shoe extends JSONObject {
private String name;
private int size;
Shoe(){
// Default constructor required for calls to
// DataSnapshot.getValue(Shoe.class)
}
Shoe( String nm, int sz) { this.name = nm; this.size = sz; }
public int getSize() { return this.size; }
public void setSize(int sz) { this.size = sz; }
public String getName() { return this.name;}
public void setName(String nm) {this.name = nm; }
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
database.setPersistenceEnabled(true);
DatabaseReference myRefTarget = database.getReference("target");
Shoe obj1 = new Shoe("item ID 1", 99);
Shoe obj2 = new Shoe("item ID 2", 1000);
final Shoe obj_old = new Shoe();
Shoe obj_new = new Shoe();
DatabaseReference myRefDeviceA = myRefTarget.child("deviceA").getRef();
myRefDeviceA.keepSynced(true);
myRefDeviceA.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
obj_old.setName( dataSnapshot.getValue(Shoe.class).getName());
obj_old.setSize( dataSnapshot.getValue(Shoe.class).getSize());
Log.d(TAG_CLOUD, "from onDataChange: deviceA = " + obj_old.getName() + ", " + obj_old.getSize());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
// HERE
Log.d(TAG_CLOUD, "Name = " + obj_old.getName() + ", Size = " + obj_old.getSize());
}
the issue I got is that the read operation is asynchronously done..
D/FROM CLOUD: Name = null, Size = 0
D/FROM CLOUD: from onDataChange: deviceA = item ID 1, 99
how can adapt / modify the source code in such way that first "read" to give me values different than null and '0' ? "HERE" line
eg.
Name = item ID 1 Size = 99
Thank you.
You don't suppose to perform networking operations on the UI thread.
If you want to display the data in the activity, you should show a loading dialog in the onCreate method, and then after fetching the data close the dialog and update the activity view

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);
}
}
}

Firebase Listener code not being triggered

I have a RideList class that is called from an Activity class that retrieves data from a Firebase database. However, when I debug my program the code within my addValueEventListener is never being reached.
public class RideList {
private ArrayList<Ride> listofRides;
public Firebase myFirebase = new Firebase("https://luminous-torch-1510.firebaseio.com/rides");
Context context;
public RideList(Context context) {
this.context = context;
this.listofRides = new ArrayList <Ride>();
}
public ArrayList<Ride> getRides() {
Firebase.setAndroidContext(context);
// Attach an listener to read the data at our rides reference
Query queryRef = myFirebase.orderByChild("timePosted");
try {
queryRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println("There are " + snapshot.getChildrenCount() + " rides");
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
String rideString = postSnapshot.getValue().toString();
String[] rideA = rideString.split(" ");
String value;
for (int i = 0; i < rideA.length - 1; i++) {
rideA[i] = rideA[i].substring(rideA[i].indexOf("=") + 1);
rideA[i] = rideA[i].substring(0, rideA[i].indexOf(","));
}
rideA[rideA.length - 1] = rideA[rideA.length - 1].substring(rideA[rideA.length - 1].indexOf("=") + 1);
rideA[rideA.length - 1] = rideA[rideA.length - 1].substring(0, rideA[rideA.length - 1].indexOf("}"));
double numOfPassengers = Double.valueOf(rideA[6]);
double fare = Double.valueOf(rideA[4]);
double distance = Double.valueOf(rideA[3]);
String origin = rideA[7];
String destination = rideA[2];
double maxPassengers = Double.valueOf(rideA[5]);
String departTime = rideA[1];
String arrivalTime = rideA[0];
String timePosted = rideA[8];
String title = rideA[9];
String type1 = rideA[10];
boolean type;
if (type1.equals("offer"))
type = false;
else
type = true;
Ride ride = new Ride(numOfPassengers, fare, distance, origin, destination, maxPassengers, departTime, arrivalTime,
timePosted, title, type);
listofRides.add(ride);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
Thread.sleep(2000);
} catch (InterruptedException e) {
return null;
}
return listofRides;
}
}
This code is being called from an OnCreate function of an Activity class. Any idea on why the listener code is never being entered/executed?
Edit: Here is the code on how this function is being called in the activity class.
list = (ListView) findViewById(R.id.showrides_listView);
Firebase.setAndroidContext(this);
RideList rl = new RideList(this);
ArrayList arrayList = rl.getRides();
// Adapter: You need three parameters 'the context, id of the layout (it will be where the data is shown),
// and the array that contains the data
ArrayAdapter adapter = new ArrayAdapter<Ride>(getApplicationContext(), android.R.layout.simple_spinner_item, arrayList){
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
TextView text = (TextView) view.findViewById(android.R.id.text1);
text.setTextColor(Color.BLACK);
return view;
}
};
// Here, you set the data in your ListView
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
FloatingActionButton myFab = (FloatingActionButton) findViewById(R.id.showrides_fab);
myFab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startCreateRideActivity();
}
});
Your data is being loaded asynchronously (and after that continuously synchronized) from Firebase. Putting a Thread.sleep() in there is not going to change that fact.
You can easily see what happens if you add a few log statements:
public ArrayList<Ride> getRides() {
Query queryRef = myFirebase.orderByChild("timePosted");
try {
System.out.println("Adding listener");
queryRef.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
// THIS CODE IS CALLED ASYNCHRONOUSLY
System.out.println("Got data from Firebase");
}
public void onCancelled(FirebaseError firebaseError) {
}
});
System.out.println("Starting sleep");
Thread.sleep(2000);
} catch (InterruptedException e) {
return null;
}
System.out.println("Returning rides");
return listofRides;
}
The output is likely:
Adding listener
Starting sleep
Returning rides
Got data from Firebase
You're trying to make an asynchronous process synchronous, which is a recipe for headaches and a bad user experience. Instead of writing up a solution here, I'll link to an answer I wrote 15 minutes ago to the same problem: Retrieving ArrayList<Object> from FireBase inner class

Categories

Resources