Correctly check if user is logged in. Android - java

I have my login setup like this, I use retrofit to authenticate from the server and I'm using a token api authentication:
In the MainActivity.java method onCreate
apiInterface = ApiClient.getClient().create(ApiInterface.class);
User user = SharedPreferencesHelper.getUser(MainActivity.this);
if (user.getToken() == null) {
Intent login = new Intent(MainActivity.this, LoginActivity.class);
startActivity(login);
} else {
setContentView(R.layout.activity_main);
buildMain();
}
In the LoginActivity.java method on create (I'll summarize, the code is quite long)
loginbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//get login ingo
Login login = new Login(scardI, passwordI, device_name);
Call<User> call = apiInterface.LoginUser(login);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
User user = response.body();
//save user info into SharedPreferences
SharedPreferencesHelper.setUser(LoginActivity.this, user);
//check if user saved correctly by getting the user token
if (SharedPreferencesHelper.getUserToken(LoginActivity.this) != null) {
finish();
}
} else {
//show error message
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
//show error message
}
});
}
});
I'm having an issue, after the user logs in a blank page is displayed instead of the activity_main.xml however when I close the app and reopen it it takes me straight to the activity_main.xml as expected. Is there a reason that after the login it doesn't take me to the activity_main like expected.

An intent to MainActivity.java is missing on success. A blank screen is displayed because finish() is called.
if (response.isSuccessful()) {
User user = response.body();
SharedPreferencesHelper.setUser(LoginActivity.this, user);
if (SharedPreferencesHelper.getUserToken(LoginActivity.this) != null){
Intent i = new Intent(this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
startActivity(i);
}
}

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);

Intent not working with if operator Android

I try when it displays a message when I press a button to redirect me to a page, but it doesn't work. When successful, the message is displayed but does not forward me.
private void registerUser() {
try {
Account account = new Account();
account.setpAddress(edt_adress.getText().toString());
account.setpBloodGroup(edt_blood.getSelectedItem().toString());
account.setpFirstName(edt_first.getText().toString());
account.setpMidName(edt_mid.getText().toString());
account.setpFamilyName(edt_fam.getText().toString());
account.setpGender(edt_gender.getSelectedItem().toString());
account.setpPhone(edt_phone.getText().toString());
account.setpEgn(edt_egn.getText().toString());
account.setuPassword(edt_password.getText().toString());
account.setuUsername(edt_username.getText().toString());
INodeJS accountservice = RetrofitClient.getInstance().create(INodeJS.class);
Call call = accountservice.create(account);
call.enqueue(new Callback() {
#Override
public void onResponse(Call call, Response response) {
Account result = (Account) response.body();
Toast.makeText(getApplicationContext(), result.getNotes(), Toast.LENGTH_LONG).show();
if (result.getNotes().equals("Success")) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
}
// startActivity(intent);
}
#Override
public void onFailure(Call call, Throwable t) {
Toast.makeText(getApplicationContext(), getString(R.string.createfield), Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
looks like result.getNotes().equals("Success") isnt true, are you shure it should be? equals is case-sensitive so "success" doesn't equal "Success" and also " Success " - note whitespaces
maybe you can try with this statement:
result.getNotes().toLowerCase(Locale.getDefault()).trim().equals("success")
toLowerCase will make all your letters small caps and trim will remove all whitespaces at beggining and end of String
also there is equalsIgnoreCase method so it may also looks like below:
result.getNotes().trim().equalsIgnoreCase("success")

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);
}
}
});
}
}

GoogleAPIClient is null even when logged in successfully

I have successfully added Google authentication in android app. I am able to login properly without any error. But when i try to logout GoogleApiClient is giving me null so that i am failing to logout successfully. I tried so many answers here but nothing worked for me. Below is the code I entered in my MainActivity.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if(ApplicationPreferences.get().isFirstTimeUser()) {
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(GmailScopes.GMAIL_READONLY))
.requestServerAuthCode(Constants.SERVER_CLIENT_ID, true)
.requestEmail()
.build();
signInButton.setSize(SignInButton.SIZE_STANDARD);
signInButton.setScopes(gso.getScopeArray());
signInButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.sign_in_button:
signIn();
break;
}
}
});
signInButton.setVisibility(View.VISIBLE);
// Build a GoogleApiClient with access to the Google Sign-In API and the
// options specified by gso.
mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this)
.enableAutoManage(MainActivity.this, MainActivity.this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.addConnectionCallbacks(MainActivity.this)
.build();
mGoogleApiClient.connect();
} else {
loadMainActivity();
}
}
}, 2000);
Below is my signOut method as specified by Documentation but i failed to understand their statement You must confirm that GoogleApiClient.onConnected has been called before you call signOut. Need some idea what I am doing wrong here.
if (mGoogleApiClient != null)
{
Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
ApplicationPreferences.get().clearAll();
Intent intent = getIntent();
finish();
startActivity(intent);
}
});
}
My OnStart()
#Override
public void onStart() {
super.onStart();
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (opr.isDone()) {
// If the user's cached credentials are valid, the OptionalPendingResult will be "done"
// and the GoogleSignInResult will be available instantly.
Log.d(TAG, "Got cached sign-in");
GoogleSignInResult result = opr.get();
handleSignInResult(result);
} else {
// If the user has not previously signed in on this device or the sign-in has expired,
// this asynchronous branch will attempt to sign in the user silently. Cross-device
// single sign-on will occur in this branch.
showProgressDialog();
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(GoogleSignInResult googleSignInResult) {
hideProgressDialog();
handleSignInResult(googleSignInResult);
}
});
}
}
The error, if you say callbacks are not being called, might just be the sign that you may not have connected first. I imagine you have implemented the callbacks and they are not being called?
In any case, to "handle" the error situation, you may want to check if the GoogleApiClient is connected.. like this:
if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
{
Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(
new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
ApplicationPreferences.get().clearAll();
Intent intent = getIntent();
finish();
startActivity(intent);
}
});
}
else{
Log.w("SomeTag", "It looks like GoogleApiClient is not connected");
}
But I do think you need to check if there are any errors (for instance does onConnectionFailed(ConnectionResult result) get called instead? What error do you see?
Hope this helps.

Getting a Malformed access token "t ​ype":"OAuthException","code":190

I am writing an android application to get the Facebook user albums and photos and display in my Android application.
I have created a Facebook App with APP_ID 281846961912565.
While creating the Facebook instance, I am passing this id as follows
facebook = new Facebook(APP_ID);
Using this instance, I am able to login to my FB account post on messages on facebook wall programatically.
After logging in, I get an access_token.
I'm using the access token to get the album ids using facebook.request("https://graph.facebook.com/me/albums?access_token="+facebook.getAccessToken());
Now I get {"error":{"message":"Malformed access token ACCESSTOKENACCESSTOKEN?access_token=ACCESSTOKENACCESSTOKEN","t‌​ype":"OAuthException","code":190}}
Can any of you please help me resolve this issue and point out what i am doing wrong.
My code is as follows:
private static final String[] PERMISSIONS = new String[] { "publish_stream","user_photos" };
public boolean saveCredentials(Facebook facebook) {
Editor editor = getApplicationContext().getSharedPreferences(KEY,
Context.MODE_PRIVATE).edit();
editor.putString(TOKEN, facebook.getAccessToken());
editor.putLong(EXPIRES, facebook.getAccessExpires());
return editor.commit();
}
public boolean restoreCredentials(Facebook facebook) {
SharedPreferences sharedPreferences = getApplicationContext()
.getSharedPreferences(KEY, Context.MODE_PRIVATE);
facebook.setAccessToken(sharedPreferences.getString(TOKEN, null));
facebook.setAccessExpires(sharedPreferences.getLong(EXPIRES, 0));
return facebook.isSessionValid();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
facebook = new Facebook(APP_ID);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.facebook_dialog);
String facebookMessage = getIntent().getStringExtra("facebookMessage");
if (facebookMessage == null) {
facebookMessage = "Test wall post";
}
messageToPost = facebookMessage;
}
R.layout.facebook_dialog is the dialog which pops up asking if a message should be shared on facebook or not. If yes the following method is called.
public void share(View button) {
if (!facebook.isSessionValid()) {
loginAndPostToWall();
} else {
postToWall(messageToPost);
}
}
public void loginAndPostToWall() {
facebook.authorize(this, PERMISSIONS, Facebook.FORCE_DIALOG_AUTH,
new LoginDialogListener());
}
class LoginDialogListener implements DialogListener {
public void onComplete(Bundle values) {
saveCredentials(facebook);
if (messageToPost != null) {
postToWall(messageToPost);
}
}
public void onFacebookError(FacebookError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onError(DialogError error) {
showToast("Authentication with Facebook failed!");
finish();
}
public void onCancel() {
showToast("Authentication with Facebook cancelled!");
finish();
}
}
public void postToWall(String message) {
Bundle parameters = new Bundle();
parameters.putString("message", message);
parameters.putString("description", "topic share");
try {
facebook.request("me");
String response = facebook.request("me/feed", parameters, "POST");
Log.d("Tests", "got response: " + response);
if (response == null || response.equals("")
|| response.equals("false")) {
showToast("Blank response.");
} else {
showToast("Message posted to your facebook wall!");
}
getImagesFromUserAlbum();
finish();
} catch (Exception e) {
showToast("Failed to post to wall!");
e.printStackTrace();
finish();
}
}
Later when I do a `private void getImagesFromUserAlbum() {
facebook.getAccessToken();
JSONArray albumss = null;
String response = null;
try {
response = facebook.request("me/albums");
// `
I get the error
{"error":{"message":"Malformed access token ACCESSTOKEN?access_token=ACCESSTOKEN","type":"OAuthException","code":190}}
Thanks for your help.
The code above is now the working copy. Thanks to Bartek.
If you look at the Errors page in the documentation you will see that when you get error 190 you should authorise/reauthorise the user.
I suspect that this happened to you because you first logged in, then added the permissions to access the albums to your application BUT did not log out and log back in. Hence, you need to obtain a new access token which will grant the new permissions to your application.
Please check is there &expires in your access token if yes then remove it because it is not part of access_token and try after that.

Categories