How to switch between activities on Android using the Java programming language? - java

I want to make a login program using Firebase authentication using an email and password. First, I created a register page with the class name "RegisterActivity." When the user successfully registers, the user will then be thrown to the login page. Everything was running smoothly until I wrote the following code in the LoginActivity class.
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
// Check whether there are users who have logged in or not logged out.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null){
Intent intent = new Intent(LoginActivity.this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
finish();
}
}
};
After I apply the code above, when the user successfully registers on the register page, the user is automatically thrown to the home page instead of the login page again.
I know maybe I have to delete the code above, but if the code is deleted, then every time the user closes the application, the user will be asked to re-login. So, how do solve this issue without removing the preceding code?
This is the code snippet in the RegisterActivity class.
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
progressBar.setVisibility(View.GONE);
if (task.isSuccessful()){
Toast.makeText(RegisterActivity.this, "Register succces", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}else{
Toast.makeText(RegisterActivity.this, "Register failed", Toast.LENGTH_SHORT).show();
}
}
});

Related

How to stop firebase logout on back button? [duplicate]

This question already has answers here:
One time login in app - FirebaseAuth
(3 answers)
Closed 1 year ago.
Before you remove my question from stackoverflow, please hear me out.
I know that problem is very common but none that i tried helped me. i already saw available solutions on stackoverflow and other websites but they didn't resolved my issue.
So even if you choose to remove my question from this forum please help me resolve my question first, atleast mail me.
button1.setOnClickListener(view -> {
login();
});
}
public void login() {
Intent intent = new Intent(this, IndexActivity.class);
String mail = et1.getText().toString();
String password = et2.getText().toString();
if (mail.isEmpty()) {
error.setText(e1);
} else if (password.isEmpty()) {
error.setText(e2);
} else if (password.length() < 6) {
error.setText("Invalid Password Length!!");
et2.setError("Password length must be at least 6!!");
} else {
mAuth.signInWithEmailAndPassword(mail, password).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Toast.makeText(MainActivity.this, "Welcome Back",Toast.LENGTH_LONG).show();
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
error.setText("");
et1.setText("");
et2.setText("");
}
else {
Toast.makeText(MainActivity.this, " "+ Objects.requireNonNull(task.getException()).getMessage(),Toast.LENGTH_LONG).show();
error.setText(e3);
}
});
}
if (error.getText().toString().isEmpty()) {
error.setVisibility(View.INVISIBLE);
} else {
error.setVisibility(View.VISIBLE);
}
}
}
This above is my login page
and below is my child activity
logout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mAuth.getCurrentUser() != null) {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
mAuth.signOut();
startActivity(intent);
finish();
}
else {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
}
}
});
i have tried everything on internet. sharedPreferences, onBackPressed override, onSaveInstanceState, onRestoreInstanceState, onStart and onResume override, but i don't know what exactly to use.
check if user is already logged in or not by
if (auth.getCurrentUser() != null)
//user logged in already, do your work here for logged in user
else
//user is not logged in, let user login
The back button most likely does not log the user out, but rather the UI elements have not updated with the user information.
If you suspect the user is being logged out, this would be an Auth refresh, which will trigger an onAuthStateChanged() event if you have one registered.
Otherwise, checking the current auth for the current user with auth().currentUser should yield null or a user object
Get current user
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// User is signed in
} else {
// No user is signed in
}
Auth State Listener
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
} else {
// User is signed out
}
Log.d("LOG_Login", "onAuthStateChanged:signed_out");
}
}
};
mAuth.addAuthStateListener(mAuthListener);

Data Retrieval from Firestore is erratic

I'm working on an Android app that does the following:
Upon app start-up, it checks if a user is logged in, using AuthStateListener.
If there is a user logged in, it retrieves data from Firestore. The user data is stored in a document that I named with the following nomenclature: "User " + user's_email_ID. For example, if a user has an email ID xyz#gmail.com, his data will be stored in the document named: User xyz#gmail.com.
All documents are within the collection named "Users".
If all the fields are null/ empty in the user's data document, the app opens an Activity that asks him/her to fill all the details. Else, it takes the user to the main page (StudentMainActivity if the user is a student, or ProfessorMainActivity if the user is a professor).
Coming to my problem:
The block of code which checks whether the fields are empty has some erratic and unpredictable behavior. I'm not sure if this is a problem based on Firestore, or on the fact that data retrieval happens on a different thread.
I checked the Firestore database and saw that all fields were filled. However, when a user (who's already logged in) starts the app, the app knows that it is the same user (i.e. he's not prompted to sign in, because AuthStateListener does its job), but instead of being redirected to either StudentMainActivity or ProfessorMainActivity (the main screens), he's asked to fill his details again.
What's more confusing is that this bug doesn't always occur. There are times when the app does what is expected, i.e. take the user to the main screen, but the next time he starts the app, he's again taken to the activity that asks him to enter his details.
Source Code:
LoginActivity.java (Only the relevant parts)
//AuthStateListener is in onCreate
authStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null){
UIDEmailID = user.getEmail();
updateUI(user);
}
else{
updateUI(null);
}
}
};
private void updateUI(FirebaseUser user){
// Update UI after login
if (user != null) {
Toast.makeText(LoginActivity.this, "User " + UIDEmailID, Toast.LENGTH_LONG).show();
db.collection("Users").document("User " + UIDEmailID).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.get("department") != null || // if any
documentSnapshot.get("phoneNumber") != null || // field in
documentSnapshot.get("name") != null || // Firestore is
documentSnapshot.get("studentSemester") != null || // non-null then
documentSnapshot.get("dateOfBirth") != null || // proceed to
documentSnapshot.get("university") != null) { // further activities
if (documentSnapshot.get("userType") == "Lecturer/ Professor") {
Intent intent = new Intent(LoginActivity.this, ProfessorMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
else {
Intent intent = new Intent(LoginActivity.this, StudentMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
Toast.makeText(LoginActivity.this, "We need some additional details before we go ahead.", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(LoginActivity.this, GFBDetailsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(LoginActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
I'm sorry for the long question; I just tried to make it super descriptive. Some help would be greatly appreciated.
P.S. The reason I think this is a problem involving the usage of multiple threads is because whenever the app runs as expected (i.e. takes the user to the main screen), the toast "We need some additional details before we go ahead." appears as well. If you look at the code (the last "else" block) you will realise that it is in a seperate conditional block altogether, and thus isn't even supposed to show up if the main screen (which is in another conditional block) shows up.
EDIT 1:
I'm enclosing screenshots pertaining to the problem. Ignore the bland UI :P
This is what's expected (Comes under the second 'else' block). It is supposed to show up only if the user is logging in for the first time, i.e. does not have his data stored in a Firestore document.
The background is StudentMainActivity (inside the nested 'else'). However, even the Toast is displayed (it belongs to a seperate block altogether).
So it turns out Firestore wasn't (entirely) at fault.
Every activity in an Android application has a life span, and every time an activity is run, it goes through an elaborate sequence of lifecycle functions.
An activity's lifecycle is as follows:
Launched --> onCreate() --> onStart() --> onResume() --> Running --> onPause() --> onStop() --> onDestroy() --> Finished
I won't be digressing by going into the details of each function, because the function names are quite intuitive and self-explanatory.
As you can see in the code snippet in the question, onAuthStateChanged() is inside onCreate(). My Document ID on Firebase is of the form "User UIDEmailID", where UIDEmailID is the email ID of the user. And UIDEmailID gets updated only in onAuthStateChanged() (which, in turn, is inside onCreate()), i.e. only when the activity starts afresh, after the app has been closed and opened again.
Therefore, I updated UIDEmailID in onStart() as well, which means that every time an app is resumed, it will retrieve the email ID of the user, which can subsequently be used to retrieve the document from Firestore.
Also, I slightly tweaked my Firestore data retrieval bit of code upon advice from Nibrass H. The solution is as follows:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
running = true;
if (savedInstanceState != null){
running = savedInstanceState.getBoolean("running");
wasrunning = savedInstanceState.getBoolean("wasrunning");
}
setContentView(R.layout.splash_screen);
firebaseAuth = FirebaseAuth.getInstance();
db = FirebaseFirestore.getInstance();
authStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth1) {
FirebaseUser user = firebaseAuth1.getCurrentUser();
if (user != null){
UIDEmailID = user.getEmail();
updateUI(user);
} else {
updateUI(null);
}
}
};
}
#Override
protected void onStart() {
super.onStart();
firebaseAuth.addAuthStateListener(authStateListener);
if (firebaseAuth.getCurrentUser() != null) {
UIDEmailID = firebaseAuth.getCurrentUser().getEmail();
updateUI(firebaseAuth.getCurrentUser());
} else {
updateUI(null);
}
}
#Override
protected void onRestart() {
super.onRestart();
authStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth1) {
FirebaseUser user = firebaseAuth1.getCurrentUser();
if (user != null) {
UIDEmailID = user.getEmail();
updateUI(user);
} else {
updateUI(null);
}
}
};
}
#Override
protected void onPause() {
super.onPause();
wasrunning = running;
running = false;
}
#Override
protected void onResume() {
super.onResume();
if (wasrunning){
running = true;
}
}
#Override
protected void onStop() {
super.onStop();
if (authStateListener != null) {
firebaseAuth.removeAuthStateListener(authStateListener);
}
}
private void updateUI(FirebaseUser firebaseUser){
if (firebaseUser != null){
Toast.makeText(this, "User " + firebaseUser.getEmail(), Toast.LENGTH_SHORT).show();
db.collection("Users").document("User " + UIDEmailID).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot.get("userType") != null) {
if (documentSnapshot.get("userType").equals("Lecturer/ Professor")){
Intent intent = new Intent(SplashScreenActivity.this, ProfessorMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
finish();
startActivity(intent);
} else {
Intent intent = new Intent(SplashScreenActivity.this, StudentMainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
finish();
startActivity(intent);
}
} else {
Toast.makeText(SplashScreenActivity.this, "We need some additional details before we go ahead.", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(SplashScreenActivity.this, GFBDetailsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
finish();
startActivity(intent);
}
}
});
}
}

Change the Activity with another by Intent in mAuth.signInWithEmailAndPassword method

I try to create this app for my lesson , It sign in successfully but when i try to change the Activity to another one , app crashes. this is my Login method , its in LoginActivity.java and in onCreate() method
private void Login(String email, String password) {
auth.signInWithEmailAndPassword(email, password).addOnCompleteListener(this , new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
else {
Toast.makeText(LoginActivity.this, "Authentication failed!", Toast.LENGTH_SHORT).show();
}
}
});
}
The value of task.isSuccessful() is true , and sign in is successfully , but can`t change the activity.
As per your crash logs,
You are getting NPE on setting an action bar title.
since your theme is No action bar, so it will give NPE.
Just remove below the line, no crash would occur
getSupportActionBar().setTitle("Login");

Firebase reflect changes directly from database?

In my Firebase application I created a fragment that allows users to update their information like namn & email and so far all is going well, however my issue is after the user have updated the information - the changes are not visible untill next relaunch of the application.
How can I reflect the changes directly from the databse without promoting the User to relaunch the app?
I have created a Method called restart(); that will like the name says says restart the application - But still the changes are not being reflected!
/**
* Update Name Only
*/
private void updateDisplayNameOnly() {
showProgress();
AuthCredential credential = EmailAuthProvider
.getCredential(FirebaseAuth.getInstance().getCurrentUser().getEmail(), mConfirm.getText().toString());
FirebaseAuth.getInstance().getCurrentUser().reauthenticate(credential)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
UserProfileChangeRequest profileUpdate = new UserProfileChangeRequest.Builder()
.setDisplayName(mName.getText().toString())
//.setPhotoUri(Uri.parse("https://avatarfiles.alphacoders.com/862/86285.jpg"))
.build();
user.updateProfile(profileUpdate);
Log.d(TAG, "onComplete: User Profile updated");
Toast.makeText(getActivity(), "Name is updated", Toast.LENGTH_SHORT).show();
restartApp();
} else {
Toast.makeText(getActivity(), "Name was not updated", Toast.LENGTH_SHORT).show();
}
hideProgress();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
hideProgress();
Toast.makeText(getActivity(), "You have entered wrong password", Toast.LENGTH_SHORT).show();
}
});
}
Restart Method
public void restartApp() {
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
Firebase has a listener onDataChange(), so when you query data from firebase, make sure you implement that, see doc. If you want to reflect the change, implement it in this method (like resetting the text fields). There is no need for a restart method.
Update profile call is asynchronous, and its results are not available immediately, so for some time you'll still observe "obsolete" data.
When you call updateProfile, you get a task as a result. You can subscribe on completion of this task, and if it's completed successfully, then you will be able to get updated data from user instance. E.g.:
final FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
Log.i("MyActivity", "before updateProfile: username=" + currentUser.getDisplayName());
UserProfileChangeRequest profileUpdate = new UserProfileChangeRequest.Builder()
.setDisplayName("UPDATED_NAME")
.build();
final Task<Void> task = currentUser.updateProfile(profileUpdate);
Log.i("MyActivity", "after updateProfile: username=" + currentUser.getDisplayName());
task.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.i("MyActivity", "onComplete: username=" + currentUser.getDisplayName());
}
});
And here is an output:
I/MyActivity: before updateProfile: username=old name
I/MyActivity: after updateProfile: username=old name
I/MyActivity: onComplete: username=UPDATED_NAME
Also there is a method user.reload(), which force reload user data from Firebase server. This is useful when your client user cache is obsolete for some reason. It is also an asynchronous method, which gives you Task and you need to subscribe on its completion.

Pressing back button shows same activity

I have two Activities.
LoginActivity
HomeScreen
I also have a splash screen with this setting:
<activity android:name=".SplashScreen"
android:theme="#style/AppTheme.NoActionBar"
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
The Splash Screen then transfers the user to the HomeScreen by using this code:
Intent intent = new Intent(SplashScreen.this, HomeScreen.class);
Inside the HomeScreen Activity, I check if the user is already signed in or not. If he's not signed in, I transfer him to the LoginActivity:
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(FirebaseAuth firebaseAuth) {
if(firebaseAuth.getCurrentUser() == null)
{
Intent intent = new Intent(HomeScreen.this,LoginActivity.class);
Toast.makeText(HomeScreen.this,"H>L on no login",Toast.LENGTH_LONG).show(); //toast to show me why my app is in infinite loop
startActivity(intent);
}
}
};
On the LoginActivity, I have a simple Login button which uses FireBase Google Authentication. Also, I check if the user is already signed in or not, and if he is, he's transferred to HomeScreen Activity:
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if(firebaseAuth.getCurrentUser() != null)
{
Intent intent = new Intent(LoginActivity.this,HomeScreen.class);
Toast.makeText(LoginActivity.this,"L>H already signed in",Toast.LENGTH_LONG).show();
startActivity(intent);
}
}
};
And, also:
private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
Log.d(TAG, "firebaseAuthWithGoogle:" + acct.getId());
AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent intent = new Intent(LoginActivity.this,HomeScreen.class);
Toast.makeText(LoginActivity.this,"L>H on login",Toast.LENGTH_LONG).show();
startActivity(intent);
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(LoginActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
}
});
}
Now, the problem is: When I open the app, I get a splash screen and I'm transferred to the Login page. Then when I press back, I get the Login page AGAIN instead of exiting the app. Also, once I'm singed in: I get the splash screen and I'm taken to the HomeScreen. Then if I press back, I am taken to the HomeScreen AGAIN instead of exiting the app.
Please help me out. I searched StackOverflow, but didn't quite reach anywhere. Any help is appreciated. Ask me for updates if you need more info! :)
Okay, So I found a solution. Apart from the precious replies below, it was also necessary to add a flag to the intent saying FLAG_ACTIVITY_CLEAR_TOP.
This is the working code:
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent intent = new Intent(LoginActivity.this,HomeScreen.class);
Toast.makeText(LoginActivity.this,"L>H on login",Toast.LENGTH_LONG).show();
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(LoginActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
}
}
I am not sure it is correct, please try,
Instead of calling startActivity(new Intent(HomeScreen.this,LoginActivity.class)) from onAuthStateChanged(), simply call finish(). Hope it may help you.
Simply do like this.
img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
You need to implement onBackPressed() and by default your app should exit. However, to exit your app you can use
getActivity.finish();
or
finishAndRemoveTask(); - it finishes all activities and removes it from recent tasks list

Categories