Firebase login/registration process for a dating app
$begingroup$
I am developing another bigger project, I want to try a simple DatingApp and currently I try to develop a Login and Registration Form. There is no special register / login button, the code will later decide wether email is registered (-> login) or not (-> register). But even though it's working I am not sure if I make things to complex:
First I want so show a very simple overview of the logic I try to develop:
The App launches with the LogoActivity as a simple SplashScreen. In here the Activity decides which Activity and Fragment is about to be displayed next. It has a sharedElementTransition connected to the AuthOptionActivity.
The AuthOptionActivity only contains the Logo and a fragment Container.
Depending on the AuthStatus it will show the AuthOptionFragment, containing a Layout with two Buttons. Here you can decide which way you want to use for your registration.
In case there is for example an email-registered user but he is not verified yet the AuthOptionActivity will show the MailAuthFragment instead of the AuthOptionFragment.
The MailAuthOptionFragment itself contains another two ChildFragments.
The EnterMailSubFragment contains two EditText-Views and the ConfirmMailSubFragment is just a Text containing the Email Adress and a clickable resend textview (not implemented yet).
The code so far implemented is working but it is not as clear as I want it to be.
Logo Activity
public class LogoActivity extends AppCompatActivity {
private ImageView mLogo;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
this.init();
}
private void init(){
mLogo = findViewById(R.id.activity_logo_logo);
AuthHandler.performStartUpCheck(new AuthHandler.StartupAuthStatus() {
@Override
public void noUser() {
Toast.makeText(LogoActivity.this, "No User", Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onError(String e) {
Toast.makeText(LogoActivity.this, e, Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onPhone() {
Toast.makeText(LogoActivity.this, "Phone", Toast.LENGTH_SHORT).show();
//direct to loadingActivity
}
@Override
public void onEmail(boolean isVerified) {
Toast.makeText(LogoActivity.this, "Email" + String.valueOf(isVerified), Toast.LENGTH_SHORT).show();
if(isVerified){
//direct to LoadingActivity
}else{
performSharedElementTransactionToAuthOption();
}
}
});
//this.performSharedElementTransaction();
}
private void performSharedElementTransactionToAuthOption(){
//check for auth and proceed to Loading
Intent pIntent = new Intent(LogoActivity.this, AuthOptionActivity.class);
ActivityOptions mOptions = ActivityOptions.makeSceneTransitionAnimation(LogoActivity.this, mLogo, mLogo.getTransitionName());
startActivity(pIntent, mOptions.toBundle());
}
private void performTransactionToLoadingScreen(){
/**dummy**/
}
@Override
protected void onStop() {
//Call finish here to avoid flickering
this.finish();
super.onStop();
}
}
Nothing so special here, I am quite happy with that class. Disregard some comments, they are just for me as a reminder.
AuthHandler
public class AuthHandler {
public interface StartupAuthStatus{
void noUser();
void onError(String e);
void onPhone();
void onEmail(boolean isVerified);
}
public interface SignInStatus{
void onEmail(boolean isVerified);
}
private FirebaseAuth mAuth;
private FirebaseFirestore mFirestore;
/**STARTUP PROCEDURE**/
public static void performStartUpCheck(final StartupAuthStatus mCallback){
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
checkStartupAuthStatus(mAuth, mCallback);
}
private static void checkStartupAuthStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
mAuth = FirebaseAuth.getInstance();
checkForCurrentUser(mAuth,mCallback);
}
private static void checkForCurrentUser(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser()!=null){
checkRegistrationMethod(mAuth, mCallback);
}else{
mCallback.noUser();
}
}
private static void checkRegistrationMethod(final FirebaseAuth mAuth, final StartupAuthStatus mCallback){
if(mAuth.getUid() == null){
mCallback.onError("NullPointerInUID");
return;
}
FirebaseFirestore.getInstance().collection("Registration").document(mAuth.getUid()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if(task.getResult() != null && task.getResult().exists()){
Map<String, Object> mData = task.getResult().getData();
String mRegistrationForm = (String) mData.get("reg");
if(mRegistrationForm.equals("mail")){
checkMailVerificationStatus(mAuth, mCallback);
}else if(mRegistrationForm.equals("phone")){
mCallback.onPhone();
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
mCallback.onError(e.getMessage());
mAuth.signOut();
}
});
}
private static void checkMailVerificationStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser().isEmailVerified()){
mCallback.onEmail(true);
}else{
mCallback.onEmail(false);
}
}
/**LOGIN PROCEDURE**/
public static void enterUserWithEmailAndPassword(String mEmail, String mPassword){
FirebaseAuth mAuth = FirebaseAuth.getInstance();
createUserWithEmailAndPassword(mAuth, mEmail, mPassword);
}
private static void createUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void signUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void sendVerificationEmail(FirebaseAuth mAuth){
}
private static void setupRegistrationModel(String mModelType){
}
/**DELETE PROCEDURE**/
}
This class is made to "outsource" the code mess. Here I am not sure if it's a common or even usefull way to handle this. The Database contains Users (with a lot of Data later) and a Pool of Registrations, here I only store the choosen registration form (field reg(String): "phone" or "mail").
I need to seperate it somehow as the email-registrated-user needs to verify the email.
AuthOptionActivity
public class AuthOptionActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authoption);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
getWindow().setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.move));
}
this.checkAuthStatus();
}
@Override
public void onBackPressed() {
findViewById(R.id.activity_authoption_logo).setTransitionName(null);
super.onBackPressed();
/** Problem occured: SharedViewElement will be visible after App was closed.
* Reason: The View will try to perform the ExitTransition
* Solution 1: Delete super.onBackPressed(); and override with finish();
* Solution 2: Set Transitionname to Null**/
}
/**The SlideEnter Transition is variable, on First Startup we want a smooth slide in,
* for phoneauth we want a Left-Side Slide and for Email a Right-Side Slide.
* The Exit Animation is always slide-out-bottom-smooth.
* The Reenter Animation is always slide-in-bottom-smooth and due to overlapping
* the ExitTransition of the View before BackPress will be hidden and is always 0**/
private void performFragmentTransaction(Fragment mFragment, String mTag, Boolean mAddToBackStack, int mEnterTransition){
FragmentTransaction mFragmentTransaction = getSupportFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterTransition, 0, R.anim.slide_in_bottom_smooth, 0);
if(mAddToBackStack){
mFragmentTransaction.addToBackStack(mTag);
}
mFragmentTransaction.replace(R.id.activity_authoption_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**Check Auth Status: if CurrentUser != null but not Email verified -> Show MailAuth**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser()!=null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
//very bad way to force a backstack, still looking for a solution
this.showAuthOptionFragment();
this.showMailAuth();
}else{
this.showAuthOptionFragment();
}
}
/**Handle Fragment Transaction including Listener**/
private void showAuthOptionFragment(){
this.performFragmentTransaction(AuthOptionFragment.newInstance(new AuthOptionFragment.ClickListener() {
@Override
public void onMail() {
showMailAuth();
}
@Override
public void onPhone() {
showPhoneAuth();
}
}), "AuthOption", false, R.anim.slide_in_bottom);
}
private void showPhoneAuth(){
this.performFragmentTransaction(_PhoneAuthFragment.newInstance(new _PhoneAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//PhoneAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "PhoneAuth", true, R.anim.slide_in_left_smooth);
}
private void showMailAuth(){
this.performFragmentTransaction(_MailAuthFragment.newInstance(new _MailAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//MailAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "MailAuth", true, R.anim.slide_in_right_smooth);
}
}
For this activity I have some questions:
- I am checking the Auth Status & email again to decide which fragment to show. It's easy and simple, but repetitive (it will occur again in MailAuthFragment). Is it a common way to repetitive use FirebaseAuth instances?
- I have seen a couple of fragment -> activity communication solutions using onAttach etc. Is my solution to pass a CustomListener(interface) in the newInstance method okay? Is there any bug that could occur using it that way?
I pretty much like it as it makes the handling easier.
MailAuthOptionFragment
public class _MailAuthFragment extends Fragment {
public interface AuthStatusListener{
void onCancel();
void onSuccess();
}
private AuthStatusListener mCallback;
private String mActiveWindow;
private CheckCancelButton mCheckCancelButton;
private String mEmail;
private String mPassword;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.fragment_mailauth, container, false);
this.checkAuthStatus();
this.initButton(mView);
return mView;
}
/**PUBLIC METHODS**/
/**Set the Fragment Listener for AuthOptionActivity**/
public void setListener(AuthStatusListener mCallback){
this.mCallback = mCallback;
}
/**Replaces hard coded Instances as Listener will be passed directly**/
public static _MailAuthFragment newInstance(AuthStatusListener mCallback){
_MailAuthFragment mFragment = new _MailAuthFragment();
mFragment.setListener(mCallback);
return mFragment;
}
/**PRIVATE METHODS**/
/**Check AuthStatus, if CurrentUser!=null but not verified -> Show VerificationScreen**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser() != null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
this.showConfirmEmailSubFragment(R.anim.slide_in_right_smooth);
}else{
this.showEnterEmailSubFragment(R.anim.slide_in_right_smooth);
}
}
/**Handle Registration Process Fragments**/
private void performFragmentTransaction(Fragment mFragment, String mTag, int mEnterAnim){
mActiveWindow = mTag;
FragmentTransaction mFragmentTransaction = getChildFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterAnim, 0);
mFragmentTransaction.replace(R.id.fragment_mailauth_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**MailAuth receives any valid TextChange from SubFragment**/
private void showEnterEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(EnterMailSubFragment.newInstance(new EnterMailSubFragment.EnterMailListener() {
@Override
public void onEmailChanged(String mEmail) {
_MailAuthFragment.this.mEmail = mEmail;
}
@Override
public void onPasswordChanged(String mPassword) {
_MailAuthFragment.this.mPassword = mPassword;
}
}), "EnterMail", mEnterAnim);
}
/**MailAuth receives resend VerificationEmailRequest from SubFragment**/
private void showConfirmEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(new ConfirmMailSubFragment(), "ConfirmMail", R.anim.slide_in_right_smooth);
}
private void initButton(final View mView){
/**Custom Button using AfterEffects, Bodymovin and LottieView, contains some commands like "transform to tick"**/
mCheckCancelButton = new CheckCancelButton((LottieAnimationView)mView.findViewById(R.id.fragment_mailauth_nextBtn));
mCheckCancelButton.setCustomClickListener(new CheckCancelButton.CheckCancelButtonClickListener() {
@Override
public void onClick() {
switch(mActiveWindow){
case "EnterMail":
// the received Email and Password will be passed to AuthHandlerClass - not implemented yet
break;
case "ConfirmMail":
// the AuthHandler will reloard the currentUser and check for email verification Status - not implemented yet
break;
}
}
});
}
}
Checking AuthStatus again and choosing whether to show the EnterEmailSubFragment or the ConfirmEmailSubFragment. This class contains a button handling both SubFragment pages. This leads to that switch-case method. Not sure if that is a proper way.
EnterMailSubFragment
public class EnterMailSubFragment extends Fragment {
public interface EnterMailListener{
void onEmailChanged(String mEmail);
void onPasswordChanged(String mPassword);
}
private EnterMailListener mCallback;
private EditText mEmail, mPassword;
private ImageView mEmailWarning, mPasswordWarning;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_mailauth, container, false);
this.initUiWidgets(mView);
this.initEditText(mView);
return mView;
}
public void setEnterMailListener(EnterMailListener mMailListener){
this.mCallback = mMailListener;
}
public static EnterMailSubFragment newInstance(EnterMailListener mMailListener){
EnterMailSubFragment mFragment = new EnterMailSubFragment();
mFragment.setEnterMailListener(mMailListener);
return mFragment;
}
private void initUiWidgets(View mView){
mEmail = mView.findViewById(R.id.subfragment_mailauth_email_ET);
mPassword = mView.findViewById(R.id.subfragment_mailauth_password_et);
mEmailWarning = mView.findViewById(R.id.subfragment_mailauth_email_warning);
mPasswordWarning = mView.findViewById(R.id.subfragment_mailauth_password_warning);
}
/**Any Valid Entry will be passed to MailFragment**/
private void initEditText(final View mView){
mEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mEmail.getText() != null && Patterns.EMAIL_ADDRESS.matcher(mEmail.getText().toString()).matches()){
mEmailWarning.setVisibility(View.GONE);
mCallback.onEmailChanged(mEmail.getText().toString());
}else{
mEmailWarning.setVisibility(View.VISIBLE);
}
}
});
mPassword.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mPassword.getText() != null && mPassword.getText().length() >= 6){
mPasswordWarning.setVisibility(View.GONE);
mCallback.onPasswordChanged(mPassword.getText().toString());
}else{
mPasswordWarning.setVisibility(View.VISIBLE);
}
}
});
}
}
The Textwatcher checks every entry and transfers it to the MailAuthFragment as MailAuthFragment handels the button event (this is for design puposes, I want the content to move and the button is meant to stay in its position).
ConfirmMailAuthSubFragment
public class ConfirmMailSubFragment extends Fragment {
public interface ConfirmMailListener{
void onResendEmail();
}
private ConfirmMailListener mCallback;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_confirmmail, container, false);
return mView;
}
public void setConfirmMailListener(ConfirmMailListener mCallback){
this.mCallback = mCallback;
}
public static ConfirmMailSubFragment newInstance(ConfirmMailListener mCallback){
ConfirmMailSubFragment mFragment = new ConfirmMailSubFragment();
mFragment.setConfirmMailListener(mCallback);
return mFragment;
}
}
Nothing special here yet. It will handle the Button Event "Resend Verification Mail" and transfer it to MailAuthFragment.
I know it's a very early stage of my code but as beautiful as it looks with that slide in animations I am not sure if I am running in the wrong direction or making stuff unnecessary complex.
java android authentication firebase
$endgroup$
add a comment |
$begingroup$
I am developing another bigger project, I want to try a simple DatingApp and currently I try to develop a Login and Registration Form. There is no special register / login button, the code will later decide wether email is registered (-> login) or not (-> register). But even though it's working I am not sure if I make things to complex:
First I want so show a very simple overview of the logic I try to develop:
The App launches with the LogoActivity as a simple SplashScreen. In here the Activity decides which Activity and Fragment is about to be displayed next. It has a sharedElementTransition connected to the AuthOptionActivity.
The AuthOptionActivity only contains the Logo and a fragment Container.
Depending on the AuthStatus it will show the AuthOptionFragment, containing a Layout with two Buttons. Here you can decide which way you want to use for your registration.
In case there is for example an email-registered user but he is not verified yet the AuthOptionActivity will show the MailAuthFragment instead of the AuthOptionFragment.
The MailAuthOptionFragment itself contains another two ChildFragments.
The EnterMailSubFragment contains two EditText-Views and the ConfirmMailSubFragment is just a Text containing the Email Adress and a clickable resend textview (not implemented yet).
The code so far implemented is working but it is not as clear as I want it to be.
Logo Activity
public class LogoActivity extends AppCompatActivity {
private ImageView mLogo;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
this.init();
}
private void init(){
mLogo = findViewById(R.id.activity_logo_logo);
AuthHandler.performStartUpCheck(new AuthHandler.StartupAuthStatus() {
@Override
public void noUser() {
Toast.makeText(LogoActivity.this, "No User", Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onError(String e) {
Toast.makeText(LogoActivity.this, e, Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onPhone() {
Toast.makeText(LogoActivity.this, "Phone", Toast.LENGTH_SHORT).show();
//direct to loadingActivity
}
@Override
public void onEmail(boolean isVerified) {
Toast.makeText(LogoActivity.this, "Email" + String.valueOf(isVerified), Toast.LENGTH_SHORT).show();
if(isVerified){
//direct to LoadingActivity
}else{
performSharedElementTransactionToAuthOption();
}
}
});
//this.performSharedElementTransaction();
}
private void performSharedElementTransactionToAuthOption(){
//check for auth and proceed to Loading
Intent pIntent = new Intent(LogoActivity.this, AuthOptionActivity.class);
ActivityOptions mOptions = ActivityOptions.makeSceneTransitionAnimation(LogoActivity.this, mLogo, mLogo.getTransitionName());
startActivity(pIntent, mOptions.toBundle());
}
private void performTransactionToLoadingScreen(){
/**dummy**/
}
@Override
protected void onStop() {
//Call finish here to avoid flickering
this.finish();
super.onStop();
}
}
Nothing so special here, I am quite happy with that class. Disregard some comments, they are just for me as a reminder.
AuthHandler
public class AuthHandler {
public interface StartupAuthStatus{
void noUser();
void onError(String e);
void onPhone();
void onEmail(boolean isVerified);
}
public interface SignInStatus{
void onEmail(boolean isVerified);
}
private FirebaseAuth mAuth;
private FirebaseFirestore mFirestore;
/**STARTUP PROCEDURE**/
public static void performStartUpCheck(final StartupAuthStatus mCallback){
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
checkStartupAuthStatus(mAuth, mCallback);
}
private static void checkStartupAuthStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
mAuth = FirebaseAuth.getInstance();
checkForCurrentUser(mAuth,mCallback);
}
private static void checkForCurrentUser(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser()!=null){
checkRegistrationMethod(mAuth, mCallback);
}else{
mCallback.noUser();
}
}
private static void checkRegistrationMethod(final FirebaseAuth mAuth, final StartupAuthStatus mCallback){
if(mAuth.getUid() == null){
mCallback.onError("NullPointerInUID");
return;
}
FirebaseFirestore.getInstance().collection("Registration").document(mAuth.getUid()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if(task.getResult() != null && task.getResult().exists()){
Map<String, Object> mData = task.getResult().getData();
String mRegistrationForm = (String) mData.get("reg");
if(mRegistrationForm.equals("mail")){
checkMailVerificationStatus(mAuth, mCallback);
}else if(mRegistrationForm.equals("phone")){
mCallback.onPhone();
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
mCallback.onError(e.getMessage());
mAuth.signOut();
}
});
}
private static void checkMailVerificationStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser().isEmailVerified()){
mCallback.onEmail(true);
}else{
mCallback.onEmail(false);
}
}
/**LOGIN PROCEDURE**/
public static void enterUserWithEmailAndPassword(String mEmail, String mPassword){
FirebaseAuth mAuth = FirebaseAuth.getInstance();
createUserWithEmailAndPassword(mAuth, mEmail, mPassword);
}
private static void createUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void signUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void sendVerificationEmail(FirebaseAuth mAuth){
}
private static void setupRegistrationModel(String mModelType){
}
/**DELETE PROCEDURE**/
}
This class is made to "outsource" the code mess. Here I am not sure if it's a common or even usefull way to handle this. The Database contains Users (with a lot of Data later) and a Pool of Registrations, here I only store the choosen registration form (field reg(String): "phone" or "mail").
I need to seperate it somehow as the email-registrated-user needs to verify the email.
AuthOptionActivity
public class AuthOptionActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authoption);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
getWindow().setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.move));
}
this.checkAuthStatus();
}
@Override
public void onBackPressed() {
findViewById(R.id.activity_authoption_logo).setTransitionName(null);
super.onBackPressed();
/** Problem occured: SharedViewElement will be visible after App was closed.
* Reason: The View will try to perform the ExitTransition
* Solution 1: Delete super.onBackPressed(); and override with finish();
* Solution 2: Set Transitionname to Null**/
}
/**The SlideEnter Transition is variable, on First Startup we want a smooth slide in,
* for phoneauth we want a Left-Side Slide and for Email a Right-Side Slide.
* The Exit Animation is always slide-out-bottom-smooth.
* The Reenter Animation is always slide-in-bottom-smooth and due to overlapping
* the ExitTransition of the View before BackPress will be hidden and is always 0**/
private void performFragmentTransaction(Fragment mFragment, String mTag, Boolean mAddToBackStack, int mEnterTransition){
FragmentTransaction mFragmentTransaction = getSupportFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterTransition, 0, R.anim.slide_in_bottom_smooth, 0);
if(mAddToBackStack){
mFragmentTransaction.addToBackStack(mTag);
}
mFragmentTransaction.replace(R.id.activity_authoption_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**Check Auth Status: if CurrentUser != null but not Email verified -> Show MailAuth**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser()!=null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
//very bad way to force a backstack, still looking for a solution
this.showAuthOptionFragment();
this.showMailAuth();
}else{
this.showAuthOptionFragment();
}
}
/**Handle Fragment Transaction including Listener**/
private void showAuthOptionFragment(){
this.performFragmentTransaction(AuthOptionFragment.newInstance(new AuthOptionFragment.ClickListener() {
@Override
public void onMail() {
showMailAuth();
}
@Override
public void onPhone() {
showPhoneAuth();
}
}), "AuthOption", false, R.anim.slide_in_bottom);
}
private void showPhoneAuth(){
this.performFragmentTransaction(_PhoneAuthFragment.newInstance(new _PhoneAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//PhoneAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "PhoneAuth", true, R.anim.slide_in_left_smooth);
}
private void showMailAuth(){
this.performFragmentTransaction(_MailAuthFragment.newInstance(new _MailAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//MailAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "MailAuth", true, R.anim.slide_in_right_smooth);
}
}
For this activity I have some questions:
- I am checking the Auth Status & email again to decide which fragment to show. It's easy and simple, but repetitive (it will occur again in MailAuthFragment). Is it a common way to repetitive use FirebaseAuth instances?
- I have seen a couple of fragment -> activity communication solutions using onAttach etc. Is my solution to pass a CustomListener(interface) in the newInstance method okay? Is there any bug that could occur using it that way?
I pretty much like it as it makes the handling easier.
MailAuthOptionFragment
public class _MailAuthFragment extends Fragment {
public interface AuthStatusListener{
void onCancel();
void onSuccess();
}
private AuthStatusListener mCallback;
private String mActiveWindow;
private CheckCancelButton mCheckCancelButton;
private String mEmail;
private String mPassword;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.fragment_mailauth, container, false);
this.checkAuthStatus();
this.initButton(mView);
return mView;
}
/**PUBLIC METHODS**/
/**Set the Fragment Listener for AuthOptionActivity**/
public void setListener(AuthStatusListener mCallback){
this.mCallback = mCallback;
}
/**Replaces hard coded Instances as Listener will be passed directly**/
public static _MailAuthFragment newInstance(AuthStatusListener mCallback){
_MailAuthFragment mFragment = new _MailAuthFragment();
mFragment.setListener(mCallback);
return mFragment;
}
/**PRIVATE METHODS**/
/**Check AuthStatus, if CurrentUser!=null but not verified -> Show VerificationScreen**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser() != null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
this.showConfirmEmailSubFragment(R.anim.slide_in_right_smooth);
}else{
this.showEnterEmailSubFragment(R.anim.slide_in_right_smooth);
}
}
/**Handle Registration Process Fragments**/
private void performFragmentTransaction(Fragment mFragment, String mTag, int mEnterAnim){
mActiveWindow = mTag;
FragmentTransaction mFragmentTransaction = getChildFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterAnim, 0);
mFragmentTransaction.replace(R.id.fragment_mailauth_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**MailAuth receives any valid TextChange from SubFragment**/
private void showEnterEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(EnterMailSubFragment.newInstance(new EnterMailSubFragment.EnterMailListener() {
@Override
public void onEmailChanged(String mEmail) {
_MailAuthFragment.this.mEmail = mEmail;
}
@Override
public void onPasswordChanged(String mPassword) {
_MailAuthFragment.this.mPassword = mPassword;
}
}), "EnterMail", mEnterAnim);
}
/**MailAuth receives resend VerificationEmailRequest from SubFragment**/
private void showConfirmEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(new ConfirmMailSubFragment(), "ConfirmMail", R.anim.slide_in_right_smooth);
}
private void initButton(final View mView){
/**Custom Button using AfterEffects, Bodymovin and LottieView, contains some commands like "transform to tick"**/
mCheckCancelButton = new CheckCancelButton((LottieAnimationView)mView.findViewById(R.id.fragment_mailauth_nextBtn));
mCheckCancelButton.setCustomClickListener(new CheckCancelButton.CheckCancelButtonClickListener() {
@Override
public void onClick() {
switch(mActiveWindow){
case "EnterMail":
// the received Email and Password will be passed to AuthHandlerClass - not implemented yet
break;
case "ConfirmMail":
// the AuthHandler will reloard the currentUser and check for email verification Status - not implemented yet
break;
}
}
});
}
}
Checking AuthStatus again and choosing whether to show the EnterEmailSubFragment or the ConfirmEmailSubFragment. This class contains a button handling both SubFragment pages. This leads to that switch-case method. Not sure if that is a proper way.
EnterMailSubFragment
public class EnterMailSubFragment extends Fragment {
public interface EnterMailListener{
void onEmailChanged(String mEmail);
void onPasswordChanged(String mPassword);
}
private EnterMailListener mCallback;
private EditText mEmail, mPassword;
private ImageView mEmailWarning, mPasswordWarning;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_mailauth, container, false);
this.initUiWidgets(mView);
this.initEditText(mView);
return mView;
}
public void setEnterMailListener(EnterMailListener mMailListener){
this.mCallback = mMailListener;
}
public static EnterMailSubFragment newInstance(EnterMailListener mMailListener){
EnterMailSubFragment mFragment = new EnterMailSubFragment();
mFragment.setEnterMailListener(mMailListener);
return mFragment;
}
private void initUiWidgets(View mView){
mEmail = mView.findViewById(R.id.subfragment_mailauth_email_ET);
mPassword = mView.findViewById(R.id.subfragment_mailauth_password_et);
mEmailWarning = mView.findViewById(R.id.subfragment_mailauth_email_warning);
mPasswordWarning = mView.findViewById(R.id.subfragment_mailauth_password_warning);
}
/**Any Valid Entry will be passed to MailFragment**/
private void initEditText(final View mView){
mEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mEmail.getText() != null && Patterns.EMAIL_ADDRESS.matcher(mEmail.getText().toString()).matches()){
mEmailWarning.setVisibility(View.GONE);
mCallback.onEmailChanged(mEmail.getText().toString());
}else{
mEmailWarning.setVisibility(View.VISIBLE);
}
}
});
mPassword.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mPassword.getText() != null && mPassword.getText().length() >= 6){
mPasswordWarning.setVisibility(View.GONE);
mCallback.onPasswordChanged(mPassword.getText().toString());
}else{
mPasswordWarning.setVisibility(View.VISIBLE);
}
}
});
}
}
The Textwatcher checks every entry and transfers it to the MailAuthFragment as MailAuthFragment handels the button event (this is for design puposes, I want the content to move and the button is meant to stay in its position).
ConfirmMailAuthSubFragment
public class ConfirmMailSubFragment extends Fragment {
public interface ConfirmMailListener{
void onResendEmail();
}
private ConfirmMailListener mCallback;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_confirmmail, container, false);
return mView;
}
public void setConfirmMailListener(ConfirmMailListener mCallback){
this.mCallback = mCallback;
}
public static ConfirmMailSubFragment newInstance(ConfirmMailListener mCallback){
ConfirmMailSubFragment mFragment = new ConfirmMailSubFragment();
mFragment.setConfirmMailListener(mCallback);
return mFragment;
}
}
Nothing special here yet. It will handle the Button Event "Resend Verification Mail" and transfer it to MailAuthFragment.
I know it's a very early stage of my code but as beautiful as it looks with that slide in animations I am not sure if I am running in the wrong direction or making stuff unnecessary complex.
java android authentication firebase
$endgroup$
add a comment |
$begingroup$
I am developing another bigger project, I want to try a simple DatingApp and currently I try to develop a Login and Registration Form. There is no special register / login button, the code will later decide wether email is registered (-> login) or not (-> register). But even though it's working I am not sure if I make things to complex:
First I want so show a very simple overview of the logic I try to develop:
The App launches with the LogoActivity as a simple SplashScreen. In here the Activity decides which Activity and Fragment is about to be displayed next. It has a sharedElementTransition connected to the AuthOptionActivity.
The AuthOptionActivity only contains the Logo and a fragment Container.
Depending on the AuthStatus it will show the AuthOptionFragment, containing a Layout with two Buttons. Here you can decide which way you want to use for your registration.
In case there is for example an email-registered user but he is not verified yet the AuthOptionActivity will show the MailAuthFragment instead of the AuthOptionFragment.
The MailAuthOptionFragment itself contains another two ChildFragments.
The EnterMailSubFragment contains two EditText-Views and the ConfirmMailSubFragment is just a Text containing the Email Adress and a clickable resend textview (not implemented yet).
The code so far implemented is working but it is not as clear as I want it to be.
Logo Activity
public class LogoActivity extends AppCompatActivity {
private ImageView mLogo;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
this.init();
}
private void init(){
mLogo = findViewById(R.id.activity_logo_logo);
AuthHandler.performStartUpCheck(new AuthHandler.StartupAuthStatus() {
@Override
public void noUser() {
Toast.makeText(LogoActivity.this, "No User", Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onError(String e) {
Toast.makeText(LogoActivity.this, e, Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onPhone() {
Toast.makeText(LogoActivity.this, "Phone", Toast.LENGTH_SHORT).show();
//direct to loadingActivity
}
@Override
public void onEmail(boolean isVerified) {
Toast.makeText(LogoActivity.this, "Email" + String.valueOf(isVerified), Toast.LENGTH_SHORT).show();
if(isVerified){
//direct to LoadingActivity
}else{
performSharedElementTransactionToAuthOption();
}
}
});
//this.performSharedElementTransaction();
}
private void performSharedElementTransactionToAuthOption(){
//check for auth and proceed to Loading
Intent pIntent = new Intent(LogoActivity.this, AuthOptionActivity.class);
ActivityOptions mOptions = ActivityOptions.makeSceneTransitionAnimation(LogoActivity.this, mLogo, mLogo.getTransitionName());
startActivity(pIntent, mOptions.toBundle());
}
private void performTransactionToLoadingScreen(){
/**dummy**/
}
@Override
protected void onStop() {
//Call finish here to avoid flickering
this.finish();
super.onStop();
}
}
Nothing so special here, I am quite happy with that class. Disregard some comments, they are just for me as a reminder.
AuthHandler
public class AuthHandler {
public interface StartupAuthStatus{
void noUser();
void onError(String e);
void onPhone();
void onEmail(boolean isVerified);
}
public interface SignInStatus{
void onEmail(boolean isVerified);
}
private FirebaseAuth mAuth;
private FirebaseFirestore mFirestore;
/**STARTUP PROCEDURE**/
public static void performStartUpCheck(final StartupAuthStatus mCallback){
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
checkStartupAuthStatus(mAuth, mCallback);
}
private static void checkStartupAuthStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
mAuth = FirebaseAuth.getInstance();
checkForCurrentUser(mAuth,mCallback);
}
private static void checkForCurrentUser(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser()!=null){
checkRegistrationMethod(mAuth, mCallback);
}else{
mCallback.noUser();
}
}
private static void checkRegistrationMethod(final FirebaseAuth mAuth, final StartupAuthStatus mCallback){
if(mAuth.getUid() == null){
mCallback.onError("NullPointerInUID");
return;
}
FirebaseFirestore.getInstance().collection("Registration").document(mAuth.getUid()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if(task.getResult() != null && task.getResult().exists()){
Map<String, Object> mData = task.getResult().getData();
String mRegistrationForm = (String) mData.get("reg");
if(mRegistrationForm.equals("mail")){
checkMailVerificationStatus(mAuth, mCallback);
}else if(mRegistrationForm.equals("phone")){
mCallback.onPhone();
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
mCallback.onError(e.getMessage());
mAuth.signOut();
}
});
}
private static void checkMailVerificationStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser().isEmailVerified()){
mCallback.onEmail(true);
}else{
mCallback.onEmail(false);
}
}
/**LOGIN PROCEDURE**/
public static void enterUserWithEmailAndPassword(String mEmail, String mPassword){
FirebaseAuth mAuth = FirebaseAuth.getInstance();
createUserWithEmailAndPassword(mAuth, mEmail, mPassword);
}
private static void createUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void signUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void sendVerificationEmail(FirebaseAuth mAuth){
}
private static void setupRegistrationModel(String mModelType){
}
/**DELETE PROCEDURE**/
}
This class is made to "outsource" the code mess. Here I am not sure if it's a common or even usefull way to handle this. The Database contains Users (with a lot of Data later) and a Pool of Registrations, here I only store the choosen registration form (field reg(String): "phone" or "mail").
I need to seperate it somehow as the email-registrated-user needs to verify the email.
AuthOptionActivity
public class AuthOptionActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authoption);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
getWindow().setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.move));
}
this.checkAuthStatus();
}
@Override
public void onBackPressed() {
findViewById(R.id.activity_authoption_logo).setTransitionName(null);
super.onBackPressed();
/** Problem occured: SharedViewElement will be visible after App was closed.
* Reason: The View will try to perform the ExitTransition
* Solution 1: Delete super.onBackPressed(); and override with finish();
* Solution 2: Set Transitionname to Null**/
}
/**The SlideEnter Transition is variable, on First Startup we want a smooth slide in,
* for phoneauth we want a Left-Side Slide and for Email a Right-Side Slide.
* The Exit Animation is always slide-out-bottom-smooth.
* The Reenter Animation is always slide-in-bottom-smooth and due to overlapping
* the ExitTransition of the View before BackPress will be hidden and is always 0**/
private void performFragmentTransaction(Fragment mFragment, String mTag, Boolean mAddToBackStack, int mEnterTransition){
FragmentTransaction mFragmentTransaction = getSupportFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterTransition, 0, R.anim.slide_in_bottom_smooth, 0);
if(mAddToBackStack){
mFragmentTransaction.addToBackStack(mTag);
}
mFragmentTransaction.replace(R.id.activity_authoption_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**Check Auth Status: if CurrentUser != null but not Email verified -> Show MailAuth**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser()!=null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
//very bad way to force a backstack, still looking for a solution
this.showAuthOptionFragment();
this.showMailAuth();
}else{
this.showAuthOptionFragment();
}
}
/**Handle Fragment Transaction including Listener**/
private void showAuthOptionFragment(){
this.performFragmentTransaction(AuthOptionFragment.newInstance(new AuthOptionFragment.ClickListener() {
@Override
public void onMail() {
showMailAuth();
}
@Override
public void onPhone() {
showPhoneAuth();
}
}), "AuthOption", false, R.anim.slide_in_bottom);
}
private void showPhoneAuth(){
this.performFragmentTransaction(_PhoneAuthFragment.newInstance(new _PhoneAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//PhoneAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "PhoneAuth", true, R.anim.slide_in_left_smooth);
}
private void showMailAuth(){
this.performFragmentTransaction(_MailAuthFragment.newInstance(new _MailAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//MailAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "MailAuth", true, R.anim.slide_in_right_smooth);
}
}
For this activity I have some questions:
- I am checking the Auth Status & email again to decide which fragment to show. It's easy and simple, but repetitive (it will occur again in MailAuthFragment). Is it a common way to repetitive use FirebaseAuth instances?
- I have seen a couple of fragment -> activity communication solutions using onAttach etc. Is my solution to pass a CustomListener(interface) in the newInstance method okay? Is there any bug that could occur using it that way?
I pretty much like it as it makes the handling easier.
MailAuthOptionFragment
public class _MailAuthFragment extends Fragment {
public interface AuthStatusListener{
void onCancel();
void onSuccess();
}
private AuthStatusListener mCallback;
private String mActiveWindow;
private CheckCancelButton mCheckCancelButton;
private String mEmail;
private String mPassword;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.fragment_mailauth, container, false);
this.checkAuthStatus();
this.initButton(mView);
return mView;
}
/**PUBLIC METHODS**/
/**Set the Fragment Listener for AuthOptionActivity**/
public void setListener(AuthStatusListener mCallback){
this.mCallback = mCallback;
}
/**Replaces hard coded Instances as Listener will be passed directly**/
public static _MailAuthFragment newInstance(AuthStatusListener mCallback){
_MailAuthFragment mFragment = new _MailAuthFragment();
mFragment.setListener(mCallback);
return mFragment;
}
/**PRIVATE METHODS**/
/**Check AuthStatus, if CurrentUser!=null but not verified -> Show VerificationScreen**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser() != null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
this.showConfirmEmailSubFragment(R.anim.slide_in_right_smooth);
}else{
this.showEnterEmailSubFragment(R.anim.slide_in_right_smooth);
}
}
/**Handle Registration Process Fragments**/
private void performFragmentTransaction(Fragment mFragment, String mTag, int mEnterAnim){
mActiveWindow = mTag;
FragmentTransaction mFragmentTransaction = getChildFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterAnim, 0);
mFragmentTransaction.replace(R.id.fragment_mailauth_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**MailAuth receives any valid TextChange from SubFragment**/
private void showEnterEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(EnterMailSubFragment.newInstance(new EnterMailSubFragment.EnterMailListener() {
@Override
public void onEmailChanged(String mEmail) {
_MailAuthFragment.this.mEmail = mEmail;
}
@Override
public void onPasswordChanged(String mPassword) {
_MailAuthFragment.this.mPassword = mPassword;
}
}), "EnterMail", mEnterAnim);
}
/**MailAuth receives resend VerificationEmailRequest from SubFragment**/
private void showConfirmEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(new ConfirmMailSubFragment(), "ConfirmMail", R.anim.slide_in_right_smooth);
}
private void initButton(final View mView){
/**Custom Button using AfterEffects, Bodymovin and LottieView, contains some commands like "transform to tick"**/
mCheckCancelButton = new CheckCancelButton((LottieAnimationView)mView.findViewById(R.id.fragment_mailauth_nextBtn));
mCheckCancelButton.setCustomClickListener(new CheckCancelButton.CheckCancelButtonClickListener() {
@Override
public void onClick() {
switch(mActiveWindow){
case "EnterMail":
// the received Email and Password will be passed to AuthHandlerClass - not implemented yet
break;
case "ConfirmMail":
// the AuthHandler will reloard the currentUser and check for email verification Status - not implemented yet
break;
}
}
});
}
}
Checking AuthStatus again and choosing whether to show the EnterEmailSubFragment or the ConfirmEmailSubFragment. This class contains a button handling both SubFragment pages. This leads to that switch-case method. Not sure if that is a proper way.
EnterMailSubFragment
public class EnterMailSubFragment extends Fragment {
public interface EnterMailListener{
void onEmailChanged(String mEmail);
void onPasswordChanged(String mPassword);
}
private EnterMailListener mCallback;
private EditText mEmail, mPassword;
private ImageView mEmailWarning, mPasswordWarning;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_mailauth, container, false);
this.initUiWidgets(mView);
this.initEditText(mView);
return mView;
}
public void setEnterMailListener(EnterMailListener mMailListener){
this.mCallback = mMailListener;
}
public static EnterMailSubFragment newInstance(EnterMailListener mMailListener){
EnterMailSubFragment mFragment = new EnterMailSubFragment();
mFragment.setEnterMailListener(mMailListener);
return mFragment;
}
private void initUiWidgets(View mView){
mEmail = mView.findViewById(R.id.subfragment_mailauth_email_ET);
mPassword = mView.findViewById(R.id.subfragment_mailauth_password_et);
mEmailWarning = mView.findViewById(R.id.subfragment_mailauth_email_warning);
mPasswordWarning = mView.findViewById(R.id.subfragment_mailauth_password_warning);
}
/**Any Valid Entry will be passed to MailFragment**/
private void initEditText(final View mView){
mEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mEmail.getText() != null && Patterns.EMAIL_ADDRESS.matcher(mEmail.getText().toString()).matches()){
mEmailWarning.setVisibility(View.GONE);
mCallback.onEmailChanged(mEmail.getText().toString());
}else{
mEmailWarning.setVisibility(View.VISIBLE);
}
}
});
mPassword.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mPassword.getText() != null && mPassword.getText().length() >= 6){
mPasswordWarning.setVisibility(View.GONE);
mCallback.onPasswordChanged(mPassword.getText().toString());
}else{
mPasswordWarning.setVisibility(View.VISIBLE);
}
}
});
}
}
The Textwatcher checks every entry and transfers it to the MailAuthFragment as MailAuthFragment handels the button event (this is for design puposes, I want the content to move and the button is meant to stay in its position).
ConfirmMailAuthSubFragment
public class ConfirmMailSubFragment extends Fragment {
public interface ConfirmMailListener{
void onResendEmail();
}
private ConfirmMailListener mCallback;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_confirmmail, container, false);
return mView;
}
public void setConfirmMailListener(ConfirmMailListener mCallback){
this.mCallback = mCallback;
}
public static ConfirmMailSubFragment newInstance(ConfirmMailListener mCallback){
ConfirmMailSubFragment mFragment = new ConfirmMailSubFragment();
mFragment.setConfirmMailListener(mCallback);
return mFragment;
}
}
Nothing special here yet. It will handle the Button Event "Resend Verification Mail" and transfer it to MailAuthFragment.
I know it's a very early stage of my code but as beautiful as it looks with that slide in animations I am not sure if I am running in the wrong direction or making stuff unnecessary complex.
java android authentication firebase
$endgroup$
I am developing another bigger project, I want to try a simple DatingApp and currently I try to develop a Login and Registration Form. There is no special register / login button, the code will later decide wether email is registered (-> login) or not (-> register). But even though it's working I am not sure if I make things to complex:
First I want so show a very simple overview of the logic I try to develop:
The App launches with the LogoActivity as a simple SplashScreen. In here the Activity decides which Activity and Fragment is about to be displayed next. It has a sharedElementTransition connected to the AuthOptionActivity.
The AuthOptionActivity only contains the Logo and a fragment Container.
Depending on the AuthStatus it will show the AuthOptionFragment, containing a Layout with two Buttons. Here you can decide which way you want to use for your registration.
In case there is for example an email-registered user but he is not verified yet the AuthOptionActivity will show the MailAuthFragment instead of the AuthOptionFragment.
The MailAuthOptionFragment itself contains another two ChildFragments.
The EnterMailSubFragment contains two EditText-Views and the ConfirmMailSubFragment is just a Text containing the Email Adress and a clickable resend textview (not implemented yet).
The code so far implemented is working but it is not as clear as I want it to be.
Logo Activity
public class LogoActivity extends AppCompatActivity {
private ImageView mLogo;
private Handler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
this.init();
}
private void init(){
mLogo = findViewById(R.id.activity_logo_logo);
AuthHandler.performStartUpCheck(new AuthHandler.StartupAuthStatus() {
@Override
public void noUser() {
Toast.makeText(LogoActivity.this, "No User", Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onError(String e) {
Toast.makeText(LogoActivity.this, e, Toast.LENGTH_SHORT).show();
performSharedElementTransactionToAuthOption();
}
@Override
public void onPhone() {
Toast.makeText(LogoActivity.this, "Phone", Toast.LENGTH_SHORT).show();
//direct to loadingActivity
}
@Override
public void onEmail(boolean isVerified) {
Toast.makeText(LogoActivity.this, "Email" + String.valueOf(isVerified), Toast.LENGTH_SHORT).show();
if(isVerified){
//direct to LoadingActivity
}else{
performSharedElementTransactionToAuthOption();
}
}
});
//this.performSharedElementTransaction();
}
private void performSharedElementTransactionToAuthOption(){
//check for auth and proceed to Loading
Intent pIntent = new Intent(LogoActivity.this, AuthOptionActivity.class);
ActivityOptions mOptions = ActivityOptions.makeSceneTransitionAnimation(LogoActivity.this, mLogo, mLogo.getTransitionName());
startActivity(pIntent, mOptions.toBundle());
}
private void performTransactionToLoadingScreen(){
/**dummy**/
}
@Override
protected void onStop() {
//Call finish here to avoid flickering
this.finish();
super.onStop();
}
}
Nothing so special here, I am quite happy with that class. Disregard some comments, they are just for me as a reminder.
AuthHandler
public class AuthHandler {
public interface StartupAuthStatus{
void noUser();
void onError(String e);
void onPhone();
void onEmail(boolean isVerified);
}
public interface SignInStatus{
void onEmail(boolean isVerified);
}
private FirebaseAuth mAuth;
private FirebaseFirestore mFirestore;
/**STARTUP PROCEDURE**/
public static void performStartUpCheck(final StartupAuthStatus mCallback){
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
checkStartupAuthStatus(mAuth, mCallback);
}
private static void checkStartupAuthStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
mAuth = FirebaseAuth.getInstance();
checkForCurrentUser(mAuth,mCallback);
}
private static void checkForCurrentUser(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser()!=null){
checkRegistrationMethod(mAuth, mCallback);
}else{
mCallback.noUser();
}
}
private static void checkRegistrationMethod(final FirebaseAuth mAuth, final StartupAuthStatus mCallback){
if(mAuth.getUid() == null){
mCallback.onError("NullPointerInUID");
return;
}
FirebaseFirestore.getInstance().collection("Registration").document(mAuth.getUid()).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task) {
if(task.getResult() != null && task.getResult().exists()){
Map<String, Object> mData = task.getResult().getData();
String mRegistrationForm = (String) mData.get("reg");
if(mRegistrationForm.equals("mail")){
checkMailVerificationStatus(mAuth, mCallback);
}else if(mRegistrationForm.equals("phone")){
mCallback.onPhone();
}
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
mCallback.onError(e.getMessage());
mAuth.signOut();
}
});
}
private static void checkMailVerificationStatus(FirebaseAuth mAuth, StartupAuthStatus mCallback){
if(mAuth.getCurrentUser().isEmailVerified()){
mCallback.onEmail(true);
}else{
mCallback.onEmail(false);
}
}
/**LOGIN PROCEDURE**/
public static void enterUserWithEmailAndPassword(String mEmail, String mPassword){
FirebaseAuth mAuth = FirebaseAuth.getInstance();
createUserWithEmailAndPassword(mAuth, mEmail, mPassword);
}
private static void createUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void signUserWithEmailAndPassword(FirebaseAuth mAuth, String mEmail, String mPassword){
}
private static void sendVerificationEmail(FirebaseAuth mAuth){
}
private static void setupRegistrationModel(String mModelType){
}
/**DELETE PROCEDURE**/
}
This class is made to "outsource" the code mess. Here I am not sure if it's a common or even usefull way to handle this. The Database contains Users (with a lot of Data later) and a Pool of Registrations, here I only store the choosen registration form (field reg(String): "phone" or "mail").
I need to seperate it somehow as the email-registrated-user needs to verify the email.
AuthOptionActivity
public class AuthOptionActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_authoption);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
getWindow().setSharedElementEnterTransition(TransitionInflater.from(this).inflateTransition(android.R.transition.move));
}
this.checkAuthStatus();
}
@Override
public void onBackPressed() {
findViewById(R.id.activity_authoption_logo).setTransitionName(null);
super.onBackPressed();
/** Problem occured: SharedViewElement will be visible after App was closed.
* Reason: The View will try to perform the ExitTransition
* Solution 1: Delete super.onBackPressed(); and override with finish();
* Solution 2: Set Transitionname to Null**/
}
/**The SlideEnter Transition is variable, on First Startup we want a smooth slide in,
* for phoneauth we want a Left-Side Slide and for Email a Right-Side Slide.
* The Exit Animation is always slide-out-bottom-smooth.
* The Reenter Animation is always slide-in-bottom-smooth and due to overlapping
* the ExitTransition of the View before BackPress will be hidden and is always 0**/
private void performFragmentTransaction(Fragment mFragment, String mTag, Boolean mAddToBackStack, int mEnterTransition){
FragmentTransaction mFragmentTransaction = getSupportFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterTransition, 0, R.anim.slide_in_bottom_smooth, 0);
if(mAddToBackStack){
mFragmentTransaction.addToBackStack(mTag);
}
mFragmentTransaction.replace(R.id.activity_authoption_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**Check Auth Status: if CurrentUser != null but not Email verified -> Show MailAuth**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser()!=null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
//very bad way to force a backstack, still looking for a solution
this.showAuthOptionFragment();
this.showMailAuth();
}else{
this.showAuthOptionFragment();
}
}
/**Handle Fragment Transaction including Listener**/
private void showAuthOptionFragment(){
this.performFragmentTransaction(AuthOptionFragment.newInstance(new AuthOptionFragment.ClickListener() {
@Override
public void onMail() {
showMailAuth();
}
@Override
public void onPhone() {
showPhoneAuth();
}
}), "AuthOption", false, R.anim.slide_in_bottom);
}
private void showPhoneAuth(){
this.performFragmentTransaction(_PhoneAuthFragment.newInstance(new _PhoneAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//PhoneAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "PhoneAuth", true, R.anim.slide_in_left_smooth);
}
private void showMailAuth(){
this.performFragmentTransaction(_MailAuthFragment.newInstance(new _MailAuthFragment.AuthStatusListener() {
@Override
public void onCancel() {
//MailAuthFragment contains a "back" Button it will fire "onCancel()"
onBackPressed();
}
@Override
public void onSuccess() {
//show LoadingScreen
}
}), "MailAuth", true, R.anim.slide_in_right_smooth);
}
}
For this activity I have some questions:
- I am checking the Auth Status & email again to decide which fragment to show. It's easy and simple, but repetitive (it will occur again in MailAuthFragment). Is it a common way to repetitive use FirebaseAuth instances?
- I have seen a couple of fragment -> activity communication solutions using onAttach etc. Is my solution to pass a CustomListener(interface) in the newInstance method okay? Is there any bug that could occur using it that way?
I pretty much like it as it makes the handling easier.
MailAuthOptionFragment
public class _MailAuthFragment extends Fragment {
public interface AuthStatusListener{
void onCancel();
void onSuccess();
}
private AuthStatusListener mCallback;
private String mActiveWindow;
private CheckCancelButton mCheckCancelButton;
private String mEmail;
private String mPassword;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.fragment_mailauth, container, false);
this.checkAuthStatus();
this.initButton(mView);
return mView;
}
/**PUBLIC METHODS**/
/**Set the Fragment Listener for AuthOptionActivity**/
public void setListener(AuthStatusListener mCallback){
this.mCallback = mCallback;
}
/**Replaces hard coded Instances as Listener will be passed directly**/
public static _MailAuthFragment newInstance(AuthStatusListener mCallback){
_MailAuthFragment mFragment = new _MailAuthFragment();
mFragment.setListener(mCallback);
return mFragment;
}
/**PRIVATE METHODS**/
/**Check AuthStatus, if CurrentUser!=null but not verified -> Show VerificationScreen**/
private void checkAuthStatus(){
if(FirebaseAuth.getInstance().getCurrentUser() != null && !FirebaseAuth.getInstance().getCurrentUser().isEmailVerified()){
this.showConfirmEmailSubFragment(R.anim.slide_in_right_smooth);
}else{
this.showEnterEmailSubFragment(R.anim.slide_in_right_smooth);
}
}
/**Handle Registration Process Fragments**/
private void performFragmentTransaction(Fragment mFragment, String mTag, int mEnterAnim){
mActiveWindow = mTag;
FragmentTransaction mFragmentTransaction = getChildFragmentManager().beginTransaction();
mFragmentTransaction.setCustomAnimations(mEnterAnim, 0);
mFragmentTransaction.replace(R.id.fragment_mailauth_container, mFragment, mTag);
mFragmentTransaction.commit();
}
/**MailAuth receives any valid TextChange from SubFragment**/
private void showEnterEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(EnterMailSubFragment.newInstance(new EnterMailSubFragment.EnterMailListener() {
@Override
public void onEmailChanged(String mEmail) {
_MailAuthFragment.this.mEmail = mEmail;
}
@Override
public void onPasswordChanged(String mPassword) {
_MailAuthFragment.this.mPassword = mPassword;
}
}), "EnterMail", mEnterAnim);
}
/**MailAuth receives resend VerificationEmailRequest from SubFragment**/
private void showConfirmEmailSubFragment(int mEnterAnim){
this.performFragmentTransaction(new ConfirmMailSubFragment(), "ConfirmMail", R.anim.slide_in_right_smooth);
}
private void initButton(final View mView){
/**Custom Button using AfterEffects, Bodymovin and LottieView, contains some commands like "transform to tick"**/
mCheckCancelButton = new CheckCancelButton((LottieAnimationView)mView.findViewById(R.id.fragment_mailauth_nextBtn));
mCheckCancelButton.setCustomClickListener(new CheckCancelButton.CheckCancelButtonClickListener() {
@Override
public void onClick() {
switch(mActiveWindow){
case "EnterMail":
// the received Email and Password will be passed to AuthHandlerClass - not implemented yet
break;
case "ConfirmMail":
// the AuthHandler will reloard the currentUser and check for email verification Status - not implemented yet
break;
}
}
});
}
}
Checking AuthStatus again and choosing whether to show the EnterEmailSubFragment or the ConfirmEmailSubFragment. This class contains a button handling both SubFragment pages. This leads to that switch-case method. Not sure if that is a proper way.
EnterMailSubFragment
public class EnterMailSubFragment extends Fragment {
public interface EnterMailListener{
void onEmailChanged(String mEmail);
void onPasswordChanged(String mPassword);
}
private EnterMailListener mCallback;
private EditText mEmail, mPassword;
private ImageView mEmailWarning, mPasswordWarning;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_mailauth, container, false);
this.initUiWidgets(mView);
this.initEditText(mView);
return mView;
}
public void setEnterMailListener(EnterMailListener mMailListener){
this.mCallback = mMailListener;
}
public static EnterMailSubFragment newInstance(EnterMailListener mMailListener){
EnterMailSubFragment mFragment = new EnterMailSubFragment();
mFragment.setEnterMailListener(mMailListener);
return mFragment;
}
private void initUiWidgets(View mView){
mEmail = mView.findViewById(R.id.subfragment_mailauth_email_ET);
mPassword = mView.findViewById(R.id.subfragment_mailauth_password_et);
mEmailWarning = mView.findViewById(R.id.subfragment_mailauth_email_warning);
mPasswordWarning = mView.findViewById(R.id.subfragment_mailauth_password_warning);
}
/**Any Valid Entry will be passed to MailFragment**/
private void initEditText(final View mView){
mEmail.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mEmail.getText() != null && Patterns.EMAIL_ADDRESS.matcher(mEmail.getText().toString()).matches()){
mEmailWarning.setVisibility(View.GONE);
mCallback.onEmailChanged(mEmail.getText().toString());
}else{
mEmailWarning.setVisibility(View.VISIBLE);
}
}
});
mPassword.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if(mPassword.getText() != null && mPassword.getText().length() >= 6){
mPasswordWarning.setVisibility(View.GONE);
mCallback.onPasswordChanged(mPassword.getText().toString());
}else{
mPasswordWarning.setVisibility(View.VISIBLE);
}
}
});
}
}
The Textwatcher checks every entry and transfers it to the MailAuthFragment as MailAuthFragment handels the button event (this is for design puposes, I want the content to move and the button is meant to stay in its position).
ConfirmMailAuthSubFragment
public class ConfirmMailSubFragment extends Fragment {
public interface ConfirmMailListener{
void onResendEmail();
}
private ConfirmMailListener mCallback;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mView = inflater.inflate(R.layout.subfragment_confirmmail, container, false);
return mView;
}
public void setConfirmMailListener(ConfirmMailListener mCallback){
this.mCallback = mCallback;
}
public static ConfirmMailSubFragment newInstance(ConfirmMailListener mCallback){
ConfirmMailSubFragment mFragment = new ConfirmMailSubFragment();
mFragment.setConfirmMailListener(mCallback);
return mFragment;
}
}
Nothing special here yet. It will handle the Button Event "Resend Verification Mail" and transfer it to MailAuthFragment.
I know it's a very early stage of my code but as beautiful as it looks with that slide in animations I am not sure if I am running in the wrong direction or making stuff unnecessary complex.
java android authentication firebase
java android authentication firebase
edited 15 mins ago
200_success
130k17153419
130k17153419
asked 1 hour ago
J. LoJ. Lo
62
62
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215717%2ffirebase-login-registration-process-for-a-dating-app%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215717%2ffirebase-login-registration-process-for-a-dating-app%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown