Welcome User screen firebase integration - unexpected results - android

I have a key 'newUser' within my firebase database, which can be set to either 'true' or 'false', as shown here:
firebase database structure click here for screenshot
I have referenced to the database within a LoginActivity class, upon clicking a login button the userLogin method is called, within this method I perform the following:
mDatabase.orderByChild("email").equalTo(email)
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot singleSnapshot: dataSnapshot.getChildren()){
String newUser = singleSnapshot.getValue(User.class).newUser;
if(newUser.equals("true")){
Log.d("Yes!", "this is a new user");
startnewActivity(singleSnapshot);
}else {
Log.d("No!", "this is an old user");
startnewActivity(singleSnapshot);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Note that equalTo(email) ... email is a local variable which grabs the text within the textfield where the user inputs the email.
the startnewActivity method is defined below, to initialise the activities dependent on whether the user is new or not:
private void startnewActivity(DataSnapshot singleSnapshot) {
String s = singleSnapshot.getValue(User.class).newUser;
if (s.equals("true")) {
Intent i = new Intent(LoginActivity.this, AudiometryNewUser.class);
singleSnapshot.getRef().child("newUser").setValue("false");
Log.d("iterated!?", "");
startActivity(i);
return;
}
else if(s.equals("false")){
Intent i = new Intent(LoginActivity.this, MainDashboard.class);
Log.d("iterated", "");
startActivity(i);
}
}
my intention is when the user is a newUser = true, then they will be taken to the welcome screen AudiometryNewUser, and for the newUser value to adopt "false".
The result however is that the user is ALWAYS taken to the dashboard, even if they are a new user. If they were a newUser and logged in, upon clicking the back button the user is navigated to the expected welcome new user activity.
on top of this, several instances of the activity are sometimes launched.
I just wondered if anyone could explain why this was happening, and what am I failing to see?
If clarity is needed please say so, I'll do my best to update the post.
Many thanks in advance.

From experimentation and further reading:
The addValueEvent listener is called for every value changed, therefore when setting the singleSnapShot to false this was in affect changing the value, thus the listener was being called again. Instead the correct approach was to user a Listener for a single value event.
When swapped for this method, the solution works as expected.

Related

How to check value in realtime database (ON/OFF)

How i can in this code
#Override
protected void onStart() {
super.onStart();
//if the user is already signed in
//we will close this activity
//and take the user to profile activity
if (mAuth.getCurrentUser() != null) {
finish();
startActivity(new Intent(this, ActivitySplash.class));
}
}
make check whether the child (userId) is set to ON / OFF and if ON then we run the code
if (mAuth.getCurrentUser() != null) {
finish();
startActivity(new Intent(this, ActivitySplash.class));
}
if OFF then we show a specific activity.
My database
As #FrankvanPuffelen said, you should spend some time reading docs, it would help you write code yourself, still I am briefing the things for you here. It should make things more clear.
Reading from database is done by correctly referencing your desired node from the database and then using the correct eventListener. There are 3 types of eventListeners present, singleValueEventListener, valueEventListener and childEventListener.
Read more about each of them in detail in docs.
This answer can also help you understand childEventListeners.
To retrieve the value of status node you have to go sequentially through your database parent nodes, that are users and that uid with value nfe....
So in code it would look something like this:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("users").child(uid);
// uid has the value nfe...
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String status = dataSnapshot.child("status").getValue(String.class);
// compare the value of status here and do what you want
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "onCancelled", databaseError.toException());
}
});

How to retrieve data from Firebase database without listeners?

Right now I'm developing an android app, and I just started to work with Firebase.
My question is: How can I retrieve data from the firebase database, without use listeners ?
In my game, I'm saving the high scores of all the users, and I need to take the data from the database when user go into "leader-boards" page.
I saw some solutions, which is not good for my case.
One of them is:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
This solution is not good for me, because I cant afford to my app to go into the server every time high score of user is changing, because if I would have 20,000 people who playing at the same time, then the game will stuck.
so I cant use this listener, because it will make the game very slow.
My goal is to to find a way to change the high score, without alerting the other people who is currently playing the game, I mean that I need to update every user score for his own, and when user go to the "leader boards" page, only then I will go to the server.
what is the right solution here ?
Or can I use this listener in another way?
If my question is not clear, then ask me in the comment please.
Thank you !!
my lines:
public static void setUserHighScoreToServer(Context context,boolean isClassic,int scoreNum)
{
com.firebase.client.Firebase mRef;
mRef= new com.firebase.client.Firebase("...");
String name = InternalStorage.getUserName(context);
String classic = "";
if(isClassic)classic="Classic";
else classic="Arcade";
com.firebase.client.Firebase mRefChild = mRef.child(name+classic);
mRefChild.setValue(String.valueOf(scoreNum));
}
This is the OFFICIAL way to retrieve data once without listening for data changes.
// Add all scores in ref as rows
scores.addListenerForSingleValueEvent( new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot child : snapshot.getChildren()) {
...
}
}
}
more information here: https://firebase.google.com/docs/reference/android/com/google/firebase/database/DataSnapshot
If you donĀ“t need to update on real time, you can always do a Rest api call to your database.
Just do a GET call to retrieve your data
https://[PROJECT_ID].firebaseio/[path].json
and you are good to go
You can also update or create new keys using rest api calls.

Removing nodes from Firebase best practices

I made a firebase project where i have 2 android apps connected to the same Database, let's say the first app is an "user app" which i'm distributing to some friends and the second one is the "admin app"(an app that is only on my personal phone). The "user app" basically just displays some infos about events in my city, while i use the "admin app" to add/delete those events from the database
In my user app i have a pretty standard RecyclerView displaying some cards using FirebaseRecyclerAdapter. Clicking on a card leads to a new Activity which displays more info about the event, using an Intent i pass an Id as a String which loads the corrisponding node in the database and everything works just fine. The problem is that when i delete a node (from Firebase console or from amy "admin" app) sometimes the "card" in my recyclerview is still avaible and clicking on it leads to a NullPointerExeption because the object i'm looking for is obviously no longer in the database.
I'm using .keepSynced(true) on my DatabaseReference and i've set setPersistanceEnabled and i think this might be the problem.
my Adapter declaration #OnStart looks like this :
if(recyclerView.getAdapter == null){
eventAdapter = new FirebaseRecyclerAdapter<DynamicData,EventViewHolder>
(
DynamicData.class,
R.layout.event_card,
EventViewHolder.class,
eventRef.orderByChild("eName")
){
#Override
protected void populateViewHolder(EventViewHolder viewHolder, DynamicData model, int position) {
final String post_key = getRef(position).getKey();
viewHolder.setDate(model.getDate());
viewHolder.setId(post_key);
viewHolder.setName(model.geteName());
viewHolder.setTime(model.getDate());
viewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent toEvent = new Intent(getActivity(), EditEvent.class);
toEvent.putExtra("EVENT_ID",post_key);
startActivity(toEvent);
}
});
}
};
}
recyclerView.setAdapter(eventAdapter);
if(rcState != null){
recyclerView.getLayoutManager().onRestoreInstanceState(rcState);
}
so nothing really strange is going on here.
My OnSavedInstanceState :
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(BUNDLE_RECYCLER_LAYOUT, linearLayoutManager.onSaveInstanceState());
rcState = outState.getParcelable(BUNDLE_RECYCLER_LAYOUT);
}
The if(recyclerView.getAdapter() == null) clause that i use OnStart togheter with the code in OnSavedInstanceState is the only thing that makes my recycler view scroll to the right position. Without this code whenever i change activiy with the Intent (by clicking on a recyclerView Item) and then come back the recyclerview gets recreated and it starts once again from top
Important : right now i'm calling
#Override
public void onDestroy() {
super.onDestroy();
recyclerView.setAdapter(null);
eventAdapter.cleanup();
}
rather than onStop() since it's apparently the only way i can keep my recyclerView position when switching activity.
In my "admin app" when i want to delete some event from the db i use this code :
//Query fetching events older than 6 hours
Query returnsixHoursOldEvents =
eventRef.orderByChild("date").endAt(six_hours_cutoff);
returnsixHoursOldEvents.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot postSnapshot : dataSnapshot.getChildren()){
postSnapshot.getRef().removeValue();
}
}
now the problem is that since i'm deleting this events from my "admin app" i can't notify the adapter on my "user app". What happens is that sometimes (seems to happen randomly) if a friend of mine opens his app he can still see the events i just delete in his recyclerview, but if he clicks on it the app crashes since the Node in the database is no longer avaible
is keepSynced or setPersistanceEnabled the problem ? any work around ?
In a shared data model it is quite possible that one user deleted data that another user still sees, especially when one of them is not connected to Firebase for a while. Your code will have to handle that situation.
Methods like removeValue() are immune to whether the data exists or not: they just ensure that after they've execute, the data is gone.

The Activity restart after saving data in Firebase

Im trying to save user information in Firebase Database but a strange behavior happens that make the Save button to restart the same activity not the Intent that i made for the next activity .
public void userData () {
user.setFName(Fname.getText().toString());
user.setLName(Lname.getText().toString());
user.setEmail(Email.getText().toString());
user.setAddress(userAddress.getText().toString());
user.setPassword(UserInfo.getString("Password", ""));
user.setID(CicID.getText().toString());
user.setUsername(Usnm.getText().toString());
if (bAdmn.isChecked()) user.setMajor("Business Administrator");
if (BTech.isChecked()) user.setMajor("Business Tech");
if (masscom.isChecked()) user.setMajor("Mass Com");
if (Eng.isChecked()) user.setMajor("Engineering");
final String us = user.getUsername();
Log.i("Username", us);
MyDatabase1.child("USERS").child(us).setValue(user);
Intent i = new Intent(getApplicationContext() , chooseCoursesActivity.class);
startActivity(i);
}
that is the method for save button , Note that i want to update the user information in a profile Activity if the user wants to change his First name or Last name or something .
But after Clicking save button , the data is saved correctly but the intent never done . it recreate the same Activity .
in the Login activity there is aLogin button which checks username and password then attempt to log in based on data ,if login successful it goes to that Profile Activity .
Save button in Profile Activity re Do the method in Login button in Login Activity which checks everything and goes to Profile Activity which what causing the Re Create Problem .
Here is the code for login button :
MyDatabase = FirebaseDatabase.getInstance().getReference();
MyDatabase.child("USERS").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean exists = false;
for (DataSnapshot child : dataSnapshot.getChildren()) {
final Map<String, Object> model = (Map<String, Object>) child.getValue();
if (model.get("username").equals(Username.getText().toString())) {
exists = true;
Log.i("USername"," Correct");
if(exists){
MyDatabase.child("USERS").orderByChild("username").equalTo(Username.getText().toString())
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child :dataSnapshot.getChildren()){
Log.i("Password",child.getValue().toString());
Log.i( "Password",child.child("password").toString());
if(password.getText().toString().equals(child.child("password").getValue())){
Log.i("LOGIN","Success");
loginsuccessful = true ;
if(loginsuccessful){
saveCredntials(Username.getText().toString(),password.getText().toString());
Intent i2 = new Intent(getApplicationContext(), ProfileActivity.class);
i2.putExtra("loginStats",IsLoggedIn);
i2.putExtra("Username",Un);
i2.putExtra("Password",Pw);
startActivity(i2);
finish();}
}else{
Log.i("LOGIN","Failed");
loginsuccessful= false ;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
}
});
}
break;
}else {
Log.i("LOGIN","FAiled");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Sorry for making this too long but i dont know the problem .
I just solved this problem! According to your case, please avoid using addValueEventListener, because it will listen to all changes you make to the database. That's why every time you make any change to the database, the code in the addValueEventListener (startActivity) will be executed no matter you call that login function or not.
My suggestion is using addListenerForSingleValueEvent instead of addValueEventListener (Make sure you change ALL the addValueEventListener in your Android project to addListenerForSingleValueEvent), or you can even remove the listener if you don't really need it. I don't know if it is a good design, but it should be able to solve your problem.
Avoid putting startActivity on a firebase listener. startActivity execute every time a value is added or removed from firebase database.

Unable to update Parse user database with custom columns in Android

When the user clicks on the "find random opponent" button, I would like the User class in Parse to create a new field (called readyToPlay) and set the value to true. Then, start a new activity where the user is randomly matched with other users whose variable readyToPlay is also set to true.
The problem is when I click the "find random opponent" button, no new field is created. I looked at the Parse documentation and cant figure out what I am doing wrong. I have attached the relevant code below.
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, FindRandomOpponent.class);
ParseUser currentUser = ParseUser.getCurrentUser();
currentUser.put(ParseConstants.KEY_READY_TO_PLAY, true);
currentUser.saveInBackground()
startActivity(intent);
}
Edit1: I called the saveInBackground method, but my user database didn't update.
Edit2: I am currently signed in as a user, so the device does have access to the internet.
This will help. It will check your user is saved or give exception for further investigation.
ParseUser currentUser = ParseUser.getCurrentUser();
currentUser.put(ParseConstants.KEY_READY_TO_PLAY, true);
currentUser.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Intent intent = new Intent(MainActivity.this, FindRandomOpponent.class);
startActivity(intent);
} else {
//log error
}
}
});
You need to call the saveInBackground() method on your currentUser object.
Reference: Parse Docs on saving objects

Categories

Resources