Android Firebase Query for near Timestamps (and close Locations if possible) - java

I'm a newbie with Firebase and I implemented it in my earthquake sensing Android app. My structure looks like
So, everytime my app senses an earthquake it makes an entry in Firebase like above. What I want to do though is use data from other devices as well to determine if the earthquake was legit or not. Specifially, every time 1 device senses one, I wanna check the firebase if in the last minute there was another entry from another device as well (ideally I would want to also check the coordinates to see if it's at a nearby location but I saw that this is more tough to accomplish so you can skip that). Any idea of how this is possible? Basically, I just want to check if new entries within 1 minute of my entry exist (I could also make a check after 1 minute, in case my device was the first to detect it).
Thanks a lot in advance.

Firebase Realtime Database queries are a multi-step process:
Order the data on a specific property value.
Determine a start node and/or end node to return (sometimes called a slice).
Optionally limit the number of nodes to return.
In Android that'd look something like:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Earthquakes");
Query query = ref.orderByChild("timestamp").startAt("2020-11-03 18:12:00").endAt("2020-11-03 18:12:59").
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot quakeSnapshot: dataSnapshot.getChildren()) {
Log.i("DB", quakeSnapshot.getKey());
Log.i("DB", quakeSnapshot.child("eventType").getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
I highly recommend reading the Firebase documentation on reading and querying data, and taking the Codelab for Android developers.

Related

RecyclerView not loading data correctly from FirebaseFirestore

I'm creating an app on Android Studio with Java and Firestore that lets users make publications and show them on a Fragment with a RecyclerView. The users create a profile (name, avatar) that gets saved in a document named after the user UID inside a collection named "Users". The publications are saved into another collection named "Posts" (title, content, picture, avatar, name).
I wrote two queries, one to get the title and content from the posts from the "Posts" collection, as well as the UID, to then do the other query that gets the name and picture from the poster and then all of those fields go inside a "Post" object and sent to the ArrayList that fills the RecyclerView.
Problem: The posts load perfectly on the RecyclerView but the user picture (the one I'm getting from a different collection) is giving me lots of problems. Sometimes it loads, sometimes it doesn't, and sometimes it loads only for the first post or even the first three posts. I don't understand what could be wrong. I'm posting the code:
private void loadPosts() {
Query postQuery = collectionReferencePosts.orderBy("timestamp", Query.Direction.DESCENDING);
postQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (QueryDocumentSnapshot document : task.getResult()) {
Post post = new Post();
post.setTitle(document.getString("title"));
post.setContent(document.getString("content"));
post.setImage(document.getString("image"));
String posterUid = document.getString("useruid");
DocumentReference docRef = collectionReferenceUsers.document(posterUid);
docRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
post.setName(documentSnapshot.getString("name").toString());
post.setAvatar(documentSnapshot.getString("image").toString());
}
});
postList.add(post);
postAdapter.notifyDataSetChanged();
}
}
});
}
If you want to display post details, along with user details at the same time, then you have to notify the adapter about the changes when the second request for getting the name and the image is complete. So you have to move the following two lines of code:
postList.add(post);
postAdapter.notifyDataSetChanged();
Right after:
post.setAvatar(documentSnapshot.getString("image").toString());
Besides that, there is another option that you should consider using, which is called denomalization. This means that you can add the name and the image of the user inside the post document. In this way, there is no need to perform the second query, since all the data already exists inside the post document. If you're new to NoSQL databases, this might sound some kind of weird, but I assure you it's quite common practice.
Please also note, that neither solution is better than the other. According to the number of changes that can occur in your app, regarding the update of name and user image, you have to check which solution is better to be used inside your project.
You can also take a look at the following post to have a better understanding of the asynchronous nature of Firebase API:
How to return a DocumentSnapShot as a result of a method?

Getting the data from Firebase and putting it there at the same time [duplicate]

I want to make an Android chat application. So I want to know how to get data from Firebase Firestore automatically, when new document create? Actually I do not wanna use add snapshot listener because of its give real-time data changes of a single document but want to find out real time updated Firebase Firestore document. Please suggest me.
I don't know if you can do this without a snapshot listener.
Check this code, if it is what you want.
private void addRealtimeUpdate() {
DocumentReference contactListener=db.collection("PhoneBook").document("Contacts");
contactListener.addSnapshotListener(new EventListener < DocumentSnapshot > () {
#Override
public void onEvent(DocumentSnapshot documentSnapshot,
FirebaseFirestoreException e) {
if (e != null) {
Log.d("ERROR", e.getMessage());
return;
}
if (documentSnapshot != null && documentSnapshot.exists()) {
Toast.makeText(MainActivity.this, "Current data:" +
documentSnapshot.getData(), Toast.LENGTH_SHORT).show();
}
}
});
}
To solve this, I recommend you to use CollectionReference's get() method. This is the correspondent addListenerForSingleValueEvent() method from Firebase real-time database.
Executes the query and returns the results as a QuerySnapshot.
If you want to use Firebase-UI library, this is a recommended way in which you can retrieve data from a Cloud Firestore database and display it in a RecyclerView using FirestoreRecyclerAdapter.
I don't know if you can do it with firestore but with realtime database you can use the .on(). If the rest of your app is using firestore, each project can use both a cloud firestore and a realtime database. The docs are really simple.

Is it better to pass more information in an intent to an activity or call the database in the other activity

I'm making an application with Firebase where a User has to sign in to use the application. The MainActivity will only launch after the User has signed in, using FirebaseAuth and FirebaseAuth.AuthStateListener.
So in the Main Activity, I have all the information about the current User.
My question is, when navigating to other activities, is it better to pass more extras via Intents about the User (or any object) to SecondActivity, or is it better to read from the Firebase database in the SecondActivity?
(Both works fine for my app, but I'm thinking about good programming style / structure and thinking about resources, speed and performance)
Also worth mentioning, I want to keep in mind that almost all of the activities' screens / UI will be updated by the information about the current object, so I want the app to be as fast and responsive as possible. For some of the code I have in my project now, I have to use Thread.sleep(x), to let the UI wait for the database to finish reading before updating the UI. This is what I'm currently using in my Google Maps Activity.
The PK of the User is the Gmail, used for signing in. Each User has several ArrayLists (that contains objects) that will grow larger when using the application, so eventually, a lot of information will be passed.
Take a look at the example code below, but don't bother too much with the details, it's just some code that I put in there now. The important thing is the general structure of how to retrieve and pass information in each activity:
MainActivity:
Intent i = new Intent(this, SecondActivity.class);
i.putExtra("email", thisUser.getEmail());
startActivity(i);
SecondActivity:
private FirebaseDatabase Db;
private DatabaseReference destinationRef, userRef;
private String email;
private User thisUser;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.SecondActivity);
email = getIntent().getStringExtra("email");
Db = FirebaseDatabase.getInstance();
userRef = Db.getReference().child("Users");
destinationRef = Db.getReference().child("Destinations");
getInfoFromDb();
}
private void getInfoFromDb(){
userRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User u;
for(DataSnapshot data : dataSnapshot.getChildren(){
u = data.getValue(User.class);
u.setUid(data.getKey());
if(email.equalsIgnoreCase(u.getEmail())){
thisUser = u;
/* Update something else, based on this info */
}
}
}
#Override
public void onCancelled(DataSnapshot dataSnapshot) { }
});
destinationRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Destination d;
for(DataSnapshot data : dataSnapshot.getChildren()){
d = data.getValue(Destination.class);
d.setUid(data.getKey());
if(thisUser.getCurrentTrip().getDestinationName().equalsIgnoreCase(d.getDestinationName())){
/*Something else, update UI or whatever */
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) { }
});
}
Here is another (shorter) example:
MainActivity:
Intent i = new Intent(this, SecondActivity.class);
i.putExtra("email", thisUser.getEmail());
i.putExtra("rank", thisUser.getRank());
i.putExtra("points", thisUser.getPoints());
i.putExtra("someArray", thisUser.getSomeArray());
i.putStringArrayListExtra("somethingElse", thisUser.getSomeStringArrayList());
// And let's say we put in a lot more information
startActivity(i);
SecondActivity
thisEmail = getIntent().getStringExtra("email");
thisRank = getIntent().getStringExtra("rank");
thisEmail = getIntent().getIntExtra("points");
thisSomeArray = getIntent().getStringArrayExtra("someArray");
thisArrayList = getIntent().getStringArrayListExtra("somethingElse");
Which approach is better?
1) The first code example, where I read from the database in the SecondActivity to get info.
2) Send the whole thisUser-object (which can grow quite big, and serialization has to be used) from MainActivity to SecondActivity.
3) Pass more (can maybe be a lot) information from MainActivity with the Intents, to the other Activites.
Again, I'm thinking about good programming structure, practice, style and am concerned about speed, responsiveness and resources.
Thanks!
I would retrieve the data from the database, since you said it can be more information. So, its better to just retrieve the data from the database in the SecondActivity.
If you pass the data using intent it means you are sharing it between activities and not storing it.
Also lets say you want to do a query that involves more than one node (maybe two nodes under root(like a join) or nested nodes), you will have to retrieve the data from the database to be able to do that
Think of testing.
In one case, you need your test framework to pass in all this extra data to test just the second method. Coupling has been increased. The method is less cohesive, since it can’t operate independently.
In the other case, the test framework doesn’t need to pass in the extra data, because the method retrieves the required information itself. Coupling is decreased. The method is more cohesive.
My $0.02
Your question is very specific to the application being developed.
Passing data through intent or persisting it in DB is very subjective to what kind of data it is and what would be the action plan when you lose that data due to some reason.
Pros & Cons : Passing data in intent
Having data passed around in intent is good for transient or deduced data as it’s computed for taking some actions and shouldn’t be persisted or retrieved every time the activity is loaded.
Code will be more complicated as compared to DB approach as every time you traverse from one activity to another you have to add code to pass the intent or check the data passed or add new data in current activity. If system crashes you loose all data and state.
Pros & Cons : DB approach
having data in DB provides you a way to resume the state when there is crash or will act as checkpoint for future. It also means you have to maintain tables and code to handle them but it would be clean as the interfaces will be clean and defined. On same note, there can be multiple calls to save and retrieve data.
You also need to consider whether data is sensitive or not.
In nutshell, it’s purely upon your application needs and you have to identify what data is required for computation and what needs to be persisted and accordingly used mixed approach to achieve better performance with reliability.

How does Firebase config get the region/country for a device?

I have a problem and I need to know how does the firebase config gets the region/country for a user, is the region based on google account, or what ? also how does it get the region without any permission ?
I have met the problem when I try to get the device region. Here are many ways to get this region, you can use below ways:
use "ro.carrier" get the cid;
use Locale.getDefault().getCountry() get the short country name;
use (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE) to get the region of SIM card.
you may use all of them and given priority.
For Firebase, I have met a short code below, maybe this can help you:
String feature_url = "feature_config";
Log.d(TAG, "init, feature_url: " + feature_url);
mRootReference = FirebaseDatabase.getInstance().getReference();
mRootReference.child(feature_url).child(BuildConfiguration.DEBUG ? "debug" : "release")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(TAG, "onDataChange");
FeatureConfig.getInstance().updateConfig(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "onCancelled");
}
});
this ie a method used to get the DataSnapShot and then get the region information from it.
Since I dont know whether this could help you, I dont past to much code here.
Hope it helps.
This may give you some idea!!!
You have your own FCM key is strictly required for native implementation.
While setup to get FCM key user should provide region/country
How to set up your own FCM keys:
1) Log in with your Google Account
Open https://console.firebase.google.com/ and log in using your Google account (if the page asks you to do so).
2) Click on Create New Project
In the next box that appears, click on Create New project.
3) Compose the Project Name and select your country/region
You’ll now be prompted with a modal box like the one shown below. Under Project name, write the name you want and below that, select your Country/region. Once you’ve done this, click on the Create Project.
For detail go through this

Reading data from firebase for android app [duplicate]

This question already has answers here:
Is it possible to read Firebase data without attaching any listeners?
(2 answers)
Closed 7 years ago.
I have been trying to get to understand how Firebase works with Android apps. I'm trying to read data from the database. I want to get all the child nodes and their corresponding key names of a particular Node. It seems like I can only read data once an event is triggered like a new child added or some value updated. What i want i to read data out without any event happening. Is there some way to this??
Bellow is the code i use in reading!
ref0 = myFirebaseRef.child("Users");
ref0.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()){
System.out.print(String.valueOf(data.getKey())+", ");
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
thanks for the help! My code is actually correct! Just that for some strange reason the System.out.print won't display nothing.. I used Logs and voila!! it displayed the data.. I think Firebase should have named that method something else rather than onDataChange!!!! As it does the job but has a misleading convention. Thanks

Categories

Resources