So the issue I am having is I have a registration form in which users enter information (which also contains an email).
I have an onClick method called
public void onCreateAccount(View view);
when the user clicks "Register", it validates the fields on the form.
public class Foo extends AppCompatActivity {
//OTHER PRIVATE MEMBERS
private EditText etEmail;
boolean isValid;
private DatabaseReference databaseUser = FirebaseDatabase.getInstance().getReference().child("User");
#Override
protected void onCreate(Bundle savedInstanceState) {
//DOES SOME INITIALIZATION
}
public void onCreateAccount(View view){
String email = etEmail.getText().toString().trim();
if(validateEmail(email)){
String id = databaseUser.push().getKey();
User user = new User(id, email);
databaseUser.child(user.getId()).setValue(user);
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.putExtra("isCreateAccount", true);
startActivityForResult (intent,0);
}
}
private boolean validateEmail(String email) {
isValid = true;
databaseUser.orderByChild("email").equalTo(emailUserEntered).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
isValid=false;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Before inserting the record into the firebase database, I want to first check if the email already exists prior to inserting. So a person typing email = a#mail.com would not allow so.
Have an index on the email field. Then you can do a query to find by email.
That will improve performance as no need to loop through the children.
Something like:
orderByChild('email').equalTo(email).once('value').exist();
Data is loaded from Firebase asynchronously, which makes it impossible to have a method like boolean validateEmail(String email) on Android. Loading data from the database would have to be a blocking operation to enable such a method, and Android doesn't allow blocking operations (as it would leave the phone inoperable).
So instead, you have to *either( move the code to create the user into validateEmail or pass in a custom interface that you then call once the result from the database is back. The code for that last one is below.
First we'll create a custom interface that you can implement anywhere where you need to check if a user already exists.
public interface UserExistsCallback {
void onCallback(boolean value);
}
This interface can be as specific or generic as you want. It's very similar to Firebase's ValueEventListener, but this interface is just for your own use.
With this interface, we can define the validateEmail method like this:
private void validateEmail(String email, final UserExistsCallback callback) {
isValid = true;
databaseUser.orderByChild("email").equalTo(emailUserEntered)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
callback.onCallback(dataSnapshot.exists())
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
And then invoke it from onCreateAccount like this:
public void onCreateAccount(View view){
String email = etEmail.getText().toString().trim();
validateEmail(email), new UserExistsCallback() {
public void onCallback(boolean exists) {
if (!exists) {
String id = databaseUser.push().getKey();
User user = new User(id, email);
databaseUser.child(user.getId()).setValue(user);
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.putExtra("isCreateAccount", true);
startActivityForResult (intent,0);
}
})
}
}
Also see many of the (linked) answers below:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener
more questions about async loading
Stopping the user from logging in if such email exists should be simple and you may use a code like this to do so:
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("User");
boolean flag=false;
databaseReference.orderByChild("email").equalTo(emailUserEntered).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
flag=true;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return flag;
But if you want the user to be stopped from writing an email which is in database in mid-way, without authentication, it would be pretty difficult.
You'd have to run a check through the database letter by letter and not only this would decrease efficiency, it would be not so good looking at the end.
EDIT:
Data is loaded asynchronously from Firebase so you should be placing something like this right inside the if(dataSnapshot.exists()) to avoid the issue you're facing right now.
if(validateEmail(email)){
String id = databaseUser.push().getKey();
User user = new User(id, email);
databaseUser.child(user.getId()).setValue(user);
Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
intent.putExtra("isCreateAccount", true);
startActivityForResult (intent,0);
}
else
Toast(YourActivity.this, "Email already exists", LENGTH_LONG).show();
This method is for auth only: firebase.auth().fetchProvidersForEmail("emailaddress#gmail.com")
Related
In my code, when user click on a button, it calls a function.In that function data has to loaded from firebase. On successful of loading, Intent will pass to another activity with some putExtra data.
SharedPreferences pref = getApplicationContext().getSharedPreferences("My_pref", MODE_PRIVATE);
choice = pref.getInt("language", 0);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(choice==1)
{
firebase("param1","param2","English");
}
if(choice==2)
{
firebase("param1","param2","hindi");
}
}
});
public void firebase(String files,String titles,String language)
{
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
But in that case intent is only passing when I click twice.In single click Intent is not passing. where is the problem?
Any code that needs data from the database, needs to be inside the onDataChange method or be called from there.
See my answer here for a longer explanation: getContactsFromFirebase() method return an empty list.
Since you have two onDataChange methods, you'll need to make sure both have been called before starting the new activity. Using some simple flag variables to track state, you can do that with:
Declare two member fields:
boolean isTloaded, isFLoaded;
And then
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
isTloaded = false;
isFloaded = false;
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
isTloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
isFloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
And then finally:
private void startCafActivity() {
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
It might be even simpler, but I had a hard time figuring out your application logic due to the variable names. Consider using more descriptive named than t, f and caf to make it easier for others (and your future self) to understand what each part of the code does in isolation.
Firebase_database
Card_view_android
In my android app, the user creates events and details are sent to the firebase database. The event then is visible as a card view inside another activity.
I wish to make this as when the user clicks on +add me in that card, his details get added to that card. This part is ready, but I want his details to be pushed to the database as well. Can anyone help me?
FirebaseUser current_user = FirebaseAuth.getInstance().getCurrentUser();
String uid = current_user.getUid();
DatabaseReference eventRef = FirebaseDatabase.getInstance().getReference().child("Events").child("lists");
String key = eventRef.push().getKey();
final DatabaseReference groupRef = eventRef.child("").child(key);
userRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String uName = dataSnapshot.child("member").getValue().toString();
addMe.setText(uName);
groupRef.child("members").setValue(uName).addOnCompleteListener(new OnCompleteListener < Void > () {
#Override
public void onComplete(#NonNull Task < Void > task) {
Toast.makeText(v.getContext(), "Success Adding you in Event", Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(v.getContext(), "some error", Toast.LENGTH_LONG).show();
}
});
The above code just adds a member child below my firebase database. I wish to have it appended inside the key of the card view clicked.
I have been trying to retrieve data fields from my firebase database for the past 5 days without any success. This is what my database looks like:
Code for fetching data:
private void alterTextView(final String id) {
if(id!=null) {
mDatabase.child("Users").child(id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserInformation userInformation = dataSnapshot.getValue(UserInformation.class);
String name = (String) userInformation.getName();
Log.d("Main Activity",name);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mWelcomeUserMessage.setText("Welcome, "+name);
}
}`
I have no idea why the name is 'null' (refer to log).
Can someone tell me what I'm doing wrong?
PS: I have defined a blank default constructor, getters, and setters in my UserInformation.java class.
Also, mDatabase is initialized as follows:
mDatabase = FirebaseDatabase.getInstance().getReference();
dataSnapshot.getValue(Class) will only load and set values into public fields. I see you already have a default constructor.
Make name field in UserInformation class public. Also make sure the snapshot you are calling getValue on is a valid JSON representation of UserInformation class and has an exact same "name" field (it must exactly match the name field in class).
Please note:
addValueEventListener adds a listener, which will be called when data is available.
Please note that the data is available after onDataChange has been called. That's why you should set your text right after you get the data you need from the DataSnapshot. Like that:
private void alterTextView(final String id) {
if(id!=null) {
mDatabase.child("Users").child(id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserInformation userInformation = dataSnapshot.getValue(UserInformation.class);
String name = (String) userInformation.getName();
Log.d("Main Activity",name);
runOnUiThread(new Runnable() {
#Override
public void run(){
mWelcomeUserMessage.setText("Welcome, "+name);
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
I am working on android app in which I want to store unique IDs of users in firebase database, so to achieve this I have created a root, named "Users" and will store all the users unique IDs in this root.
So in order to check uniqueness I have to go through each child of "Users" and after checking the uniqueness I will add the uniqueID to "Users" root or will notify the user that uniqueId is not available.
When I check uniqueness, it's conditionals (if/else) execute before occurrence of OnChildAdded event, How to execute some code(mentioned in my code) after occurrence of OnChildAdded event
My Code
postBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!uniqueIdText.getText().toString().trim().equals("")) {
uniqueness = true;
users.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot.getKey().equals(uniqueIdText.getText().toString().trim()))
uniqueness = false;
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
else if(uniqueIdText.getText().toString().trim().equals("") )
Toast.makeText(LoginActivity.this,"Please enter id",Toast.LENGTH_LONG).show();
//======================How to execute it(code below) after occurrence of OnChildAdded event====================
if (uniqueness){
DatabaseReference usersInfo = users.child(uniqueIdText.getText().toString().trim());
startActivity(new Intent(LoginActivity.this, MainActivity.class));
}
else{
if(!uniqueIdText.getText().toString().trim().equals(""))
Toast.makeText(LoginActivity.this, "ID not available, Please enter a new one", Toast.LENGTH_SHORT).show();
}
}
});
There is no way to know when all the onChildAdded events have been called. But you can know when the first data from the database has been synchronized by using a ValueEventListener.
But in this case, I'd highly recommend taking a different approach. Downloading all UIDs to check if one value is present is incredibly inefficient and won't scale. Instead, attach a listener to the specific UID that you're looking for. If that value exists, you'll get the value. If it doesn't exist, you'll get an empty snapshot:
users.child(uniqueIdText.getText().toString()).addSingleValueEventListener(#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
uniqueness = false;
}
else {
uniqueness = true;
}
...
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled", databaseError.toException());
// ...
}
};
I am having some trouble knowing when my Firebase API call is finished. After reading the Firebase documentation I have found the following:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
I understand this to mean that only after all the onChildAdded call is finished, then the ValueEventListener is called. As a result, I thought that I can populate my RecyclerView in the onChildAdded function and then the onSingleValueListener call, I can simply finish animating my loading screen (which has started animating before this function call) and proceed. However, I have run into an issue where I put some careful System.out.println statements and found that in my case, Test 1 is called before Test 2 is ever called. This causes problems because this is actually the opposite behavior of what I wanted: I wanted the onChildAdded function to finish and then call the onSingleValueListener function that prints out Test 1 to be called. Is there any reason why this is happening? Any way around this? I would appreciate an explanation on why this is happening. Thanks!
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
final Firebase commentsRef = firebaseRef.child("/comments");
Firebase linkRef = firebaseRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Test 1");
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
linkRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
commentsRef.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Comment comment = dataSnapshot.getValue(Comment.class);
System.out.println("Test 2");
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
You may want to use the **FirebaseRecyclerAdapter** class that the Firebase team makes available in FirebaseUI-Android (see https://github.com/firebase/FirebaseUI-Android/blob/master/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java)
In your gradle file add the line below (check here for latest version number in the readme)
compile 'com.firebaseui:firebase-ui-database:0.4.3'
With this code "Firebase linkRef = firebaseRef.child("/posts/" + postId);" I could see that you're using legacy Firebase API. Its deprecated now!
Kindly update your code to new Firebase 3.x.x API.
Below two are independent async call; Based on your use-case, you can use either one of the listener to read your data.
1. linkRef.addListenerForSingleValueEvent(new ValueEventListener() {});
2. linkRef.addChildEventListener(new ChildEventListener() {});
You can refer the firebase document to get more information about database listeners.
https://firebase.google.com/docs/database/android/retrieve-data
With the following code snippet, you can retrieve and populate your list of comments.
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
DatabaseReference commentsRef = firebaseRef.child("/comments");
DatabaseReference linkRef = commentsRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Iterate through data-snapshot, and update your Adapter dataset
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
}
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
// Dismiss your loading progressbar
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Handle fail case here
}
});
Hope this would help you!