workaround for async call to read from Firebase - android

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

Related

Current value of EditText not being passed to a function from onCreate()

I am building an android app that displays the COVID19 statistics for India, I am getting the stats in JSON format from https://api.covid19india.org/data.json , this API contains data of individual states too,
Below is the snip of Json array(contains json objects representing each state) that i am requesting
as of Now i am displaying the entire data ( all states ) at a time on my screen, However i want to give the state name as the input and display the stats of only that state For eg. in the below image in place of sample i want to write a state name and the stats of that state must be displayed on click of the button.
Here is the code of mainActivity.java, I am using Volley Library for fetching data from API
public class MainActivity extends AppCompatActivity {
private TextView result;
private RequestQueue mq;
public String value;
int flag = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = findViewById(R.id.textView4);
Button parse = findViewById(R.id.button);
mq = Volley.newRequestQueue(this);
EditText text = (EditText)findViewById(R.id.state_ip);
value = text.getText().toString();
parse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
jsonParse(value);
**//How do i pass 'value' i.e the state name entered by user to jsonParse**
}
});
}
private void jsonParse(final String value) {
Log.d("val_state",value);
String url = "https://api.covid19india.org/data.json";
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("statewise");
for (int i = 0; i < jsonArray.length(); i++)
{
JSONObject st = jsonArray.getJSONObject(i);
String statename = st.getString("state");
String active = st.getString("active");
String confirmed = st.getString("confirmed");
String deaths = st.getString("deaths");
String recovered = st.getString("recovered");
if(statename.equals(value))
{
flag= 1;
}
statename = "State : " + statename;
active = "Active Cases : " + active;
confirmed = "Confirmed Cases : " + confirmed;
deaths = "Total Deaths : " + deaths;
recovered = "Total Recovered : " + recovered;
if(flag==1)
{
flag=0;
result.append(statename + "\n" + String.valueOf(active) + "\n" + String.valueOf(confirmed) + "\n" + String.valueOf(deaths) + "\n" + String.valueOf(recovered) + "\n\n\n");
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
mq.add(request);
}
}
Here , i want to pass the value of state entered by the user to the method jsonParse() so that i check the state name with the received JSON data and append it to the TextView, but when i do this , and try to log the value inside the jsonParse() method i get nothing, why is this happening , How do i implement the above ?
Your EditText value is update and has to be captured after the button is clicked.
parse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
value = text.getText().toString();
jsonParse(value);
}
});

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

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)

Database reference not been recognised

I have a for loop that is creating seats. I am trying to check if the seat was already registered(booked) in our database, so as I initiate the seat I want to get into my database and check if its already there, so I put in a method called look within the two for loops, like this
for (i = 0; i < maxRow; i++) {
for (j = 0; j < maxColumn; j++) {
final SeatMo seat = new SeatMo();
seat.row = i;
seat.column = j;
seat.status = 1;
look(seat,i,j)
seatTable[i][j] = seat;
}
}
The method look is where the reference is, when I saved the seat in the database, I named the child by the specific seat that was selected e.g col:2 row:3, here is the look method
private void look(final SeatMo seat, final int a, final int b) {
databaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("col:" + i + " row:" + j)) {
seat.status = 0;
} else {
seat.status = 1;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
In my mind I want it to check for every seat as the seat is being initiated, but for some reason which I hope you may find in the code about, when the method look is called, it skips the reference(databaseRef completely). My question is, what could be causing the reference to be skipped and how can I make sure it does not get skipped?
The databaseRef is declared in the Oncreate method, it is a field
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_seats);
if(getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
currUser = FirebaseAuth.getInstance().getCurrentUser();
Intent callingIntent = getIntent();
maxC = Integer.valueOf(callingIntent.getStringExtra("col"));
maxR = Integer.valueOf(callingIntent.getStringExtra("row"));
decks = Integer.valueOf(callingIntent.getStringExtra("deck"));
fare = Double.valueOf(callingIntent.getStringExtra("fare"));
driver = callingIntent.getStringExtra("id");
destination = callingIntent.getStringExtra("dest");
databaseRef = FirebaseDatabase.getInstance().getReference("coach-drivers" + "/" + driver + "/" + "clients" );
initSeatTable(maxC,maxR);
selectedSeats = new ArrayList<>();
mMovieSeatView1= (MovieSeatView) findViewById(R.id.seat_view_one);
deck = (Spinner) findViewById(R.id.top_bottom);
mMovieSeatView1.setSeatTable(seatTable);
mMovieSeatView1.setPresenter(this);
}
The ref has the right route
Have tried changing the route to the ref like this
databaseRef = FirebaseDatabase.getInstance().getReference().child("coach-drivers").child(driver).child("clients");
still does not get to the onDataChange() and onCancel method
This is the Json structure of my database
{
"coach-drivers": {
"iiiiiiiiiiiiiii": {
"clients": {
"col:3 row:2": "one",
"col:3 row:1:"one",
"col:1 row:3" : "one"
}
}
}
}
the iiiiiiiiiii is for the driverId, represented by driver field initiated in OnCreate method
SOLVED BY USING CLOUD FIRESTORE, STILL DON'T KNOW WHY FIREBASE REFERENCE WAS BEING SKIPPED THOUGH
To solve this, move the logic of checking if the seat was already registered inside onDataChange() method. You cannot simply use seat.status outside that method because the value will be always null due the asynchronous behaviour of the method.
Having all the logic inside that method, will solve the issue. If want to use the value outside, please see my answer from this post.
Put your reference inside your look method
Also keep seatTable[i][j] = seat in OnDataChange as show below:
private void look(final SeatMo seat, final int a, final int b) {
databaseRef = FirebaseDatabase.getInstance().getReference("coach-drivers" + "/" + driver + "/" + "clients" );
databaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("col:" + i + " row:" + j)) {
seat.status = 0;
} else {
seat.status = 1;
}
seatTable[i][j] = seat;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}

How to provide two Integer values and emit one String

I am learning RxAndroid api, so I created an example where the input is two integers and the output should be a string containing these integers. for example,
for 1 and two the output should be "1,2" and so on.
in the below code is my attempts to achieve what I am planning to do, but I need guiding and help please.
code:
public class MainActivity extends AppCompatActivity {
private final static String TAG = MainActivity.class.getSimpleName();
private EditText mEditTextValue1 = null;
private EditText mEditTextValue2 = null;
private Button mButtonStartAsyncTask = null;
private rx.Observable<Integer> mAsyncObservable = null;
private TextView mTextViewProcessedValue = null;
Subscriber<String> mAsyncSubscriber = new Subscriber<String>() {
#Override
public void onCompleted() {
Log.w(TAG, "onCompleted(mAsyncSubscriber)");
}
#Override
public void onError(Throwable e) {
Log.w(TAG, "onError(mAsyncSubscriber)");
}
#Override
public void onNext(String next) {
Log.w(TAG, "onNext(mAsyncSubscriber)");
mTextViewProcessedValue.setText();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
}
private void initViews() {
mEditTextValue1 = (EditText) findViewById(R.id.editTextValue1);
mEditTextValue2 = (EditText) findViewById(R.id.editTextValue2);
mButtonStartAsyncTask = (Button) findViewById(R.id.buttonStartAsyncTask);
mButtonStartAsyncTask.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer value1 = Integer.valueOf(mEditTextValue1.getText().toString());
Integer value2 = Integer.valueOf(mEditTextValue2.getText().toString());
mAsyncObservable = rx.Observable.just(value1, value2)
.delay(3, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.doOnNext(items-> {
mTextViewProcessedValue = (TextView) findViewById(R.id.textViewProcessedValue);
return value1 + ", " + value2;
})
.doOnCompleted(new Action0() {
#Override
public void call() {
Log.w(TAG, "doOnCompleted");
}
})
//.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((Action1<? super Integer>) mAsyncSubscriber);
}
});
}
}
The operator you want to use really depends on how many numbers you will have, how many products you want, and what you want to do with the final answer. For this example, we'll use scan(...)
apply a function to each item emitted by an Observable, sequentially, and emit each successive value
Observable.just(value1, value2)
.delay(3, TimeUnit.SECONDS, AndroidSchedulers.mainThread())
.scan("", (string, integer) -> string + "," + integer)
The above will emit once: "{value1},{value2}".
If you add a third value, it will emit twice : "{value1},{value2}" and "{value1},{value2},{value3}"
i'm new with RxJava too ... this might be what you want
io.reactivex.Observable.range(1, 100).subscribe(new DisposableObserver<Integer>() {
List<Integer> list = new ArrayList<>(2);
#Override
public void onNext(Integer integer) {
list.add(integer);
if (list.size() % 2 == 0) {
Log.i(TAG, "onNext: couple int :" + list.get(0) + " ," + list.get(1));
list.clear();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
if you want to emit two values together you may emit a array or list or Pojo.
using zip operator example:
io.reactivex.Observable<Integer> s1 = io.reactivex.Observable.range(1, 100);
io.reactivex.Observable<Integer> s2 = io.reactivex.Observable.range(-100, 100);
io.reactivex.Observable.zip(s1, s2, new BiFunction<Integer, Integer, String >() {
#Override
public String apply(Integer i1, Integer i2) throws Exception {
return i1 + " ," + i2;
}})
.subscribe(System.out::println);
Looking on code what you trying achieve is reading from 2 fields on button click and emit them to observable, is it correct?
Please check this project to replace listener with rxjava bindings:
https://github.com/JakeWharton/RxBinding. Then code would be something like this:
RxView.clicks(button)
.map(event -> {
Integer value1 = Integer.valueOf(mEditTextValue1.getText().toString());
Integer value2 = Integer.valueOf(mEditTextValue2.getText().toString());
return value1 + "," + value2;
})
.subscribe(combinedIntegers ->
mTextViewProcessedValue.setText(combinedIntegers));

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