Hello
i have a method that verificate whether a phone number or an email is already exists in the database , if so user will get a toast says its already registred and will go back to signUp activity if not it will go on with the code,
public class VerficatePhone extends AppCompatActivity {
private String mVerficationId,phoneNumber,sentCode;
private User user;
private EditText phoneCodeEditText;
private PhoneAuthProvider.ForceResendingToken mResend;
private Button activate;
private TextView backToSignupText;
private ProgressBar pBar;
private FirebaseAuth mAuth;
private DatabaseReference reff;
private boolean executed =false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verficate_phone);
phoneCodeEditText=findViewById(R.id.verificationEditText);
activate=findViewById(R.id.activate);
backToSignupText=findViewById(R.id.backToSignupText);
pBar=findViewById(R.id.progressBar);
mAuth=FirebaseAuth.getInstance();
reff= FirebaseDatabase.getInstance().getReference().child("User");
//getting the user that has been sent from the sign up Activity and the phone number
if(getIntent().getSerializableExtra("userFromSignUp")!=null)
{
user=(User)getIntent().getSerializableExtra("userFromSignUp");
phoneNumber=user.getPhoneNumber();
}
else
{
phoneNumber="000";
Toast.makeText(this,"Error",Toast.LENGTH_SHORT).show();
}
if(!executed)
{
checkPhoneNumIfExists(user);
executed =true;
}
sendCode(phoneNumber);
}
my checkPhoneNumIfExists method is like this
private void checkPhoneNumIfExists (final User currentUser)
{
Log.i("checkPhoneNumIfExists method","processing");
FirebaseDatabase fDb=FirebaseDatabase.getInstance();
DatabaseReference dbRef=fDb.getReference();
dbRef.child("User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> children=dataSnapshot.getChildren();
for(DataSnapshot child:children)
{
if(currentUser!=null && child!=null)
{
User user= child.getValue(User.class);
if(user!=null && !user.getPhoneNumber().isEmpty())
{
if(currentUser.getPhoneNumber().equals(user.getPhoneNumber()) || currentUser.geteMail().equals(user.geteMail()))
{
Toast.makeText(VerficatePhone.this,"already registred",Toast.LENGTH_SHORT).show();
Intent i =new Intent(VerficatePhone.this,signUpActivity.class);
i.putExtra("userFromVerificate",currentUser);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
executed =true;
}
so here is the problem , when the user is not registred its go on with the code and excute sendCode method in onCreate method which will open a new activity ,but whenever its open the new activity from sendCode method the toast of checkPhoneNumIfExists method it shows up.
so why that would happen??
The onDataChange() is an asynchronous callback. Any data or function that relies upon it should be accessed or triggered from within the callback.
Since it looks like you're not intending to run the database check every time, I would suggest looking into SharedPreferences to persist the variable if you leave and return to the activity. You can store the value of executed when your onDataChange() executes and retrieve the value in onCreate() before your if statement.
#Override
protected void onCreate(Bundle savedInstanceState){
// ...
// If you're using SharedPreferences, retrieve the value here
if(!executed){
checkDb(user)
}else{
// I'm assuming you only want to send the code when executed is true
// which means that, for this approach, you should call sendCode()
// again inside of your onDataChange()
sendCode(phoneNum);
}
}
private void checkDb(final User currentUser){
// ...
myRef.addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
for(DataSnapshot snap: dataSnapshot.getChildren()){
// do something with the data
// ...
}
// This is where you can set the value of a variable
// or execute a function that relies upon the data of the callback.
// If you're using SharedPreferences, you would store the value here.
// You may want to call sendCode() again here
}
});
}
You can find out about SharedPreferences by visiting https://developer.android.com/training/data-storage/shared-preferences
Related
I am creating a Journal app.
I am currently working on the functionality for if the user is already logged in—bypass the "get started/log in activities"
In the video I am watching to create a journal app, the instructor calls
mUser = firebaseAuth.getCurrentUser(); several times.
He calls it in onStart(), in onCreate() and in onAuthStateChanged(). I can understand why we might need to call it again in onAuthStateChanged(), but in this case, I'm just checking if the user is already logged in, so it shouldn't change from the user received in onCreate()
I removed it from onAuthStateChanged() and onStart() and everything is still working fine. However, I'm unsure if it will lead me to errors in the future. If anyone can confirm this, I would appreciate it.
Is there a reason why we need to call getCurrentUser() several times?
Thanks.
This is my full code for reference:
public class MainActivity extends AppCompatActivity {
Button getStarted;
private FirebaseUser mUser;
private FirebaseAuth firebaseAuth;
private FirebaseAuth.AuthStateListener myListener;
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private CollectionReference myCollectionRef = db.collection("Users");
#Override
protected void onCreate(Bundle savedInstanceState) {
firebaseAuth = FirebaseAuth.getInstance();
mUser = firebaseAuth.getCurrentUser();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getStarted = findViewById(R.id.btnGetStarted);
getStarted.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, LoginActivity.class));
finish();
}
});
myListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull #NotNull FirebaseAuth firebaseAuth) {
if(mUser != null){
//logged in
//I commented this out and everything is still working fine
//mUser = firebaseAuth.getCurrentUser();
String currentUserId = mUser.getUid();
myCollectionRef.whereEqualTo("userId", currentUserId).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable #org.jetbrains.annotations.Nullable QuerySnapshot value, #Nullable #org.jetbrains.annotations.Nullable FirebaseFirestoreException error) {
if(error != null){
Log.d("my_error", error.toString());
}
if(!value.isEmpty()){
for(QueryDocumentSnapshot snapshot : value){
JournalApi myJournalApi = new JournalApi();
myJournalApi.setUsername(snapshot.getString("username"));
myJournalApi.setId(snapshot.getString("userId"));
startActivity(new Intent(MainActivity.this, JournalList.class));
finish();
}
}
}
});
}else{
//not logged in
}
}
};
}
#Override
protected void onStart() {
super.onStart();
//I commented this out and everything is still working fine
//mUser = firebaseAuth.getCurrentUser();
firebaseAuth.addAuthStateListener(myListener);
}
#Override
protected void onPause() {
super.onPause();
if(firebaseAuth != null){
firebaseAuth.removeAuthStateListener(myListener);
}
}
}
While signing in is an active process, that your code triggers by calling one of the signInWith methods, maintaining the authentication state and restoring it on application restart are background processes that happen automatically when you use the Firebase SDK. This is great, because it means you don't have to write code to keep tokens valid, or to check if the user profile has changed or their account has been disabled.
It does mean however that you can't reliably cache the value of firebaseAuth.getCurrentUser() in a variable for much time. If you keep the value in a variable, and then the SDK updates the authentication state in the background, your code may not be looking at the correct value for firebaseAuth.getCurrentUser() anymore.
That's why you'll see more calls to firebaseAuth.getCurrentUser() than you might expect, and also why you'll see firebaseAuth.addAuthStateListener in places that want to get notified when the authentication state changed, like when the user is signed in or out by the SDK.
You've to call this method everytime you want to check the user's current session due the user's session can be changed from another service or method, if you don't call this method and just call once, you won't have the latest user's profile info, auth session, and such more.
I'm currently working on a quiz app, where a user signs up with email and pw via firebase.
After the signup he shall pass a nickname which will be used in for a highscore.
So basically I want the app to add a these kind of user information, everytime a user signs up as shown in the attached picture.
In order to achieve this I used this code:
public class createNickname extends AppCompatActivity {
private static final String TAG = "createNickname";
private FirebaseDatabase mFirebaseDatabase;
private FirebaseAuth.AuthStateListener mAuthListener;
private EditText NicknameInput;
private Button NicknameReg;
private String Nickname, UserId;
private FirebaseAuth mAuth;
private String EmailEntry;
private DatabaseReference myref = mFirebaseDatabase.getInstance().getReference();
#Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_nickname);
NicknameInput = (EditText)findViewById(R.id.etNickname);
NicknameReg = (Button)findViewById(R.id.btsetNickname);
mAuth = FirebaseAuth.getInstance();
myref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange( DataSnapshot dataSnapshot ) {
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "value is: " + value);
}
#Override
public void onCancelled( DatabaseError databaseError ) {
Log.w(TAG, "Failed to read value.", databaseError.toException());
}
});
NicknameReg.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick( View view ) {
Log.d(TAG, "onClick: Attempting to add object to database.");
String newNick = NicknameInput.getText().toString();
if (!newNick.equals("")) {
FirebaseUser user = mAuth.getCurrentUser();
UserId = user.getUid();
EmailEntry = user.getEmail();
myref.child("userlist").push().setValue(UserId);
myref.child("userlist").child(UserId).push().setValue(EmailEntry);
myref.child("userlist").child(UserId).push().setValue(0);
startActivity(new Intent(getApplicationContext(), LoginActivity.class));
}
}
});
}
#Override
public void onStart() {
super.onStart();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseUser currentUser = mAuth.getCurrentUser();
}
}
So the question is now, what did I do wrong?
A user is created in the authentication window in firebase, but the database is just not updated.
Did I target the database correctly? How do u decided which database you want to target?
These are my firebase security rules:
{
"rules": {
".read": false,
".write": false
}
}
Regarding the database nothing is happening if i get it right.
I get no error message what so ever.
First I would advice to add a completion callback to your code so you know when and if your writes to Firebase have failed/succeeded.
In your case you would get a permission denied error because your security rules are set to false -> nobody has permission to read/write to your database. Instead you could give each user permission to write to their own data like this:
{
"rules": {
"userlist": {
"$user_id": {
// grants write access to the owner of this user account
// whose uid must exactly match the key ($user_id)
".write": "$user_id === auth.uid"
}
}
}
}
(See the documentation for more information)
For the above to work you will also have to change your code you are using to write to the database. Currently you are using push(). This generates a random id and you don't need (want) that here. Instead you can use setValue():
myref.child("userlist").push().setValue(UserId);
myref.child("userlist").child(UserId).child("email").setValue(EmailEntry);
myref.child("userlist").child(UserId).child("highscore").setValue(0);
You can also look at my answer here for a bit more information about this subject.
Can I define a variable globally then assign it locally and again use it as another local method in Java? I tried to fetch data from FireBase database locally. Using it I want to perform some task in another local method but the problem is I could not manage to get the data to another method. Is there any way to do it?
double slatitude,slongitude,currbal;
DatabaseReference mDatabase= FirebaseDatabase.getInstance().getReference();
DatabaseReference mref,mlat,mlong,mstatus;
protected void onCreate(#Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.travel);
// Get the Intent that started this activity and extract the string
Intent intent = getIntent();
String id = intent.getStringExtra(rfidreader.EXTRA_MESSAGE);
// Capture the layout's TextView and set the string as its text
textView = (TextView) findViewById(R.id.message);
//textView.setText(id+" "+"signed in successfully");
mref=mDatabase.child(id).child("Balance");
mlat=mDatabase.child(id).child("Start").child("Lat");
mlong=mDatabase.child(id).child("Start").child("Long");
mstatus=mDatabase.child(id).child("Status");
getData(id);
}
public void getData(String input)
{
pssngrid = input;
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
currbal = dataSnapshot.child(pssngrid).child("Balance").getValue(Double.class);
status = dataSnapshot.child(pssngrid).child("Status").getValue(Integer.class);
mlatiude = dataSnapshot.child(pssngrid).child("Start").child("Lat").getValue(Double.class);
mlongittude = dataSnapshot.child(pssngrid).child("Start").child("Long").getValue(Double.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
protected void onStart() {
super.onStart();
if(currbal>=10)
{
gps = new GPSTracker(onTravel.this);
double slatitude = gps.getLatitude();
double slongitude = gps.getLongitude();
if(status==0)
{
mlat.setValue(slatitude);
mlong.setValue(slongitude);
mstatus.setValue(1);
textView.setText("Thank you,you are eligible for travelling....");
}
else if(status==1)
{
dis=HaverSineDistance(mlatitude,mlongitude,slatitude,slongitude);
fare=dis*5;
newbal=currbal-fare;
mref.setValue(newbal);
mstatus.setValue(0);
textView.setText("Distance Traveled:"+dis+"\n"+"Current Balance:"+currbal+"\n"+"Fare:"+fare+"\n"+"Net Balance:"+newbal);
}
}
else
{
textView.setText("Sorry,you are not eligible for travelling this time.Please recharge your card..");
}
There's a difference between what you're doing, and actually making things truly "global" variables.
Private instance variables are how you share variables across methods, however you might be misunderstanding the Activity lifecycle and that Firebase does not present data to your app in any guaranteed way according to it.
In other words, onStart will likely run first. Then onCreate and getData, and only after that, does the onDataChange event occur
Now you see the issue that currbal and status are never assigned until much later that you've given.
The correct way to get data to update like this is to make the UI updates from the Firebase callback. And you don't need the fields if you pass through your needed values as parameters
#Override
protected void onStart() {
super.onStart();
}
// separate this
private updateUI(String pssngrid, DataSnapshot dataSnapshot)
double currbal = dataSnapshot.child(pssngrid).child("Balance").getValue(Double.class);
int status = dataSnapshot.child(pssngrid).child("Status").getValue(Integer.class);
double mlatiude = dataSnapshot.child(pssngrid).child("Start").child("Lat").getValue(Double.class);
double mlongittude = dataSnapshot.child(pssngrid).child("Start").child("Long").getValue(Double.class);
if(currbal>=10) {
...
And
public void getData(final String input) {
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
updateUI(input, dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Also, remove all unnecessary DatabaseReference variables. You only need the one you're querying
You may want to additionally retrieve your Firebase data as a single Java object, rather than individually extracting the fields
Each user in my app can send and get friend requests. When the user checks his friends requests, I want the app to go through each user who sent him a friend request and retrieve his information from the Realtime Database.
This is my code in order to accomplish this:
public void check_For_Friends_And_Requests(){
String loggedUser=SaveSharedPreference.getLoggedEmail(getApplicationContext());
final DatabaseReference mDatabase= FirebaseDatabase.getInstance().getReference();
final DatabaseReference userRef=mDatabase.child("Users").child(loggedUser);
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
final List<User> friendRequestsReceived_UserList=new ArrayList<>();
for (DataSnapshot postSnapshot: dataSnapshot.child("friend_requests_received").getChildren()) {
final String senderEmail=postSnapshot.getKey();
Toast.makeText(getApplicationContext(),
senderEmail, Toast.LENGTH_SHORT).show();
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Toast.makeText(getApplicationContext(),
dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
else
connectionErrorGoToMain();
}
#Override
public void onCancelled(DatabaseError databaseError) {
connectionErrorGoToMain();
}
});
}
I have in this code 2 ValueEventListeners. I add the user information to the list in the inner one. The problem is that the list is empty at the end of this process.
I would like to fill a list view with this information using these lines:
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
When I put them inside the innner listener, it works fine, but I don't want to set the adapter for each user in the list, only after the for loop.
I'm attaching a screenshot with my database structure (I don't need to get all of the parameters):
The list is empty because you are declaring friendRequestsReceived_UserList outside the inner onDataChange() method. This is happening due the asynchronous behaviour of onDataChange() method which is called before you are adding those new objects to the list. So, in order to solve this, just move the declaration of the list inside the inner onDataChange() method like this:
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final List<User> friendRequestsReceived_UserList=new ArrayList<>(); //Moved here
Toast.makeText(getApplicationContext(), dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(), R.layout.friend_requests_received_listview_row, friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
As you probably see, i set the adapter also inside the inner method. If you want to use that list outside the onDataChange() i suggest you reading my answer from this post.
I have been working with firebase and i noticed something strange.
One of the value event listener that i had registered in activity is fired even after i have finished the activity.
I have added code to open a new activity in the'onDataChanged' method , so even if i am in a different activity the new activity is opened. This is causing big problems in the app.
I know that i have to call remove listener but i expected that the listener will be removed automatically after the activity is finished. Is there a simpler way in which i can remove all the value event listeners of an activity ? Thanks in advance.
You need to remove the listener once you leave the main activity. In order to do that, you can create a method like this:
private HashMap<DatabaseReference, ValueEventListener> hashMap = new HashMap<>();
public static void removeValueEventListener(HashMap<DatabaseReference, ValueEventListener> hashMap) {
for (Map.Entry<DatabaseReference, ValueEventListener> entry : hashMap.entrySet()) {
DatabaseReference databaseReference = entry.getKey();
ValueEventListener valueEventListener = entry.getValue();
databaseReference.removeEventListener(valueEventListener);
}
}
Every time you want to remove a lister, you can use this line of code:
hashMap.put(yourRef, eventListener);
Than just call removeValueEventListener(hashMap); according to the activity life-cycle. You can add this line in your onPause() or onStop() methods. In this way you can remove all the listeners at once.
There is slightly other way
mReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Your initial code:
.....
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
mReference.removeEventListener(this);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
mReference.removeEventListener(this);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
mReference.removeEventListener(this);
}
#Override
public void onCancelled(DatabaseError databaseError) {
mReference.removeEventListener(this);
}
});
Not saying that this is the best way, but it helped me solve the problem
For those who are starting in Android Studio like me and did not understand right away Alex Mamo's answer, here comes a short example:
First declare your Firebase and HashMap variables like below.
private DatabaseReference NotifyGroupRef;
private ValueEventListener valueEventListener;
private HashMap<DatabaseReference, ValueEventListener> groupCreatorAndKeys = new HashMap<>();
After, assign your Firebase Reference in onCreate like below.
NotifyGroupRef = FirebaseDatabase.getInstance().getReference().child("NotifyGroup");
Create a function to add your listener like below.
private void updateNotifyGroupInternet(String groupCreator, String groupKey) {
NotifyGroupRef.child(groupCreator).child(groupKey).addValueEventListener(valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String[] splittedSnapshot = dataSnapshot.getValue().toString().split("=");
String clientId = splittedSnapshot[0].replace("{","");
String notification = splittedSnapshot[1].replace("}", "");
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
groupCreatorAndKeys.put(NotifyGroupRef.child(groupCreator).child(groupKey), valueEventListener);
}
Stop listening when launching a new activity like below.
#Override
public void onPause() {
super.onPause();
for (Map.Entry<DatabaseReference, ValueEventListener> entry : groupCreatorAndKeys.entrySet()) {
DatabaseReference databaseReference = entry.getKey();
ValueEventListener value = entry.getValue();
databaseReference.removeEventListener(value);
}
}
And finally, add the listener again when returning to the activity by pressing the backbutton, for example:
#Override
public void onStart() {
super.onStart();
for (Map.Entry<DatabaseReference, ValueEventListener> entry : groupCreatorAndKeys.entrySet()) {
DatabaseReference databaseReference = entry.getKey();
ValueEventListener value = entry.getValue();
databaseReference.addValueEventListener(value);
}
}
In my example, I've used different paths via variables groupCreator and groupKey and it worked perfectly!
First declare your DatabaseReference and ValueEventListener
private ValueEventListener valueEventListener;
private final DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
after that you can remove that listener in OnDestroy() or according to your activity life cycle
#Override
protected void onDestroy() {
super.onDestroy();
if(valueEventListener!=null)
{
databaseReference.removeEventListener(valueEventListener);
}
}