Why do we have to call getCurrentUser() several times Firebase? - java

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.

Related

Get firebase user id in android

After register with Firebase phone number (sms), I can get user_id and friends list from user_id, etc. But after I exit from app, and then re-enter the app return null (FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();) I cannot not get Firebase user id (firebaseUser return null). implementation 'com.google.firebase:firebase-auth:21.0.1' Plz... How to solve a problem?
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser != null) { // return null
DatabaseReference reference1 = FirebaseDatabase.getInstance().getReference("Users").child(firebaseUser.getUid());
reference1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NotNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
}
#Override
public void onCancelled(#NotNull DatabaseError databaseError) {
}
});
}
Firebase automatically restores the user credentials when you restart the app. This requires it to make a call to the server, amongst others to check whether the account was disabled. Since this call may take some time to complete, most likely it's still ongoing when you call FirebaseAuth.getInstance().getCurrentUser().
For this reason it's best to instead observe FirebaseAuth.instance.authStateChanges() as shown in the https://stackoverflow.com/collectives/google-cloud/articles/68104924/listen-for-authentication-state-in-android. This callback gets called first when the authentication check I mentioned has completed, so you can use that to respond to the restored sign-in:
auth.addAuthStateListener(new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (firebaseAuth.getCurrentUser() == null) {
Log.i("firebase", "AuthState changed to null");
}
else {
Log.i("firebase", "AuthState changed to "+firebaseAuth.getCurrentUser().getUid());
}
}
});

why this toast shows up when it should not?

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

Sign in activity keeps spawning after first run

I am building a sign-in activity (Firebase, Google sign in) that only starts on the application's first run. Problem is that activity persists on spawning even after the authentication process is complete.
I am combining Realtime Database for storing user data into the database when a new user gets registered.
I've run a debugger through the authentication process, which returned no errors or unexpected behavior. I can also confirm that Realtime Database gets queried as expected.
Application is written in the combination of Java and Kotlin.
This is how I call the SignInActivity inside MainActivity on application's first run (Java)
private SharedPreferences sharedPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Check if this is application's first run
sharedPreferences = getSharedPreferences(getApplicationContext().getPackageName(), Context.MODE_PRIVATE);
startActivity(new Intent(MainActivity.this, SignInActivity.class));
}
#Override
protected void onResume() {
super.onResume();
if (sharedPreferences.getBoolean("first", true)) {
startActivity(new Intent(MainActivity.this, SignInActivity.class));
sharedPreferences.edit().putBoolean("first", false).apply();
}
}
and this is a Kotlin function that commences the authentication process
private fun authenticateWithGoogle(account: GoogleSignInAccount?) {
if (account != null) {
val credential: AuthCredential = GoogleAuthProvider.getCredential(account.idToken, null)
firebaseAuth.signInWithCredential(credential).addOnCompleteListener{
if (it.isSuccessful) {
databaseReference.addValueEventListener(object: ValueEventListener {
override fun onCancelled(p0: DatabaseError) {}
override fun onDataChange(dataSnapshot: DataSnapshot) {
databaseReference.child(account.id!!)
val userModel: UserModel? = dataSnapshot.getValue(UserModel::class.java)
if (userModel == null) {
databaseReference.child(account.id!!).setValue(UserModel(account.id, account.displayName, account.email))
startActivity(Intent(this#SignInActivity, MainActivity::class.java))
}
startActivity(Intent(this#SignInActivity, MainActivity::class.java))
}
})
}
}
}
}
Upon the initial transition from MainActivity to SignInActivity, the authentication process begins on button press. Function authenticateWithGoogle gets called as expected and the database is queried for information whether a user exists or not. If the user exists, just transition back to MainActivity, if not, store their data to the database and then transition to MainActivity.
At this point, shared preference for storing the state of the first run in MainActivity should be set to false, but apparently, it's not, hence why SignInActivity gets called again.
Any sort of help would be very appreciated.
I fixed the problem differently, as suggested by shubham-vashisht.
So, here's how I did it:
private SharedPreferences sharedPreferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Check if it's application's first run
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean state = sharedPreferences.getBoolean("firstrun", false);
if (!state) {
Editor editor = sharedPreferences.edit();
editor.putBoolean("firstrun", true);
editor.apply();
startActivity(new Intent(MainActivity.this, SignInActivity.class));
}
}

App just opens and closes even if I initialize the context (Firebase)

So I was trying to get my first app up and running and I ran in to a problem when I tried debugging it on my phone.
The error states
Caused by: java.lang.IllegalStateException: Default FirebaseApp is not initialized in this process com.stuff.morestuff. Make sure to call FirebaseApp.initializeApp(Context) first.
at com.google.firebase.FirebaseApp.getInstance(SourceFile:218)
at com.google.firebase.auth.FirebaseAuth.getInstance(Unknown Source:1)
at com.stuff.morestuff.MainActivity.<init>(MainActivity.java:13)
And I've tried using FirebaseApp.initializeApp(this) but it still throws the same error.
public class MainActivity extends AppCompatActivity {
private FirebaseAuth mAuth;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//I've tried using this too but it didn't change anything, still the same error
//FirebaseApp.initializeApp(this);
mAuth = FirebaseAuth.getInstance();
}
#Override
public void onStart() {
super.onStart();
// Check if user is signed in (non-null) and update UI accordingly.
FirebaseUser currentUser = mAuth.getCurrentUser();
if(currentUser == null){
Intent startIntent = new Intent(MainActivity.this, StartActivity.class);
startActivity(startIntent);
}
}
}
Did I miss something?
This solution worked for me. **REMARKS Check the comments to the solution if you want to use 4.1.0
https://stackoverflow.com/a/52136056/7808468

creating a new user entry in firebase database, everytime a user signs up in Android

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.

Categories