CodeReview of a Firebase Login/Registration Process












0












$begingroup$


I am developing another bigger project, I want to try a simple DatingApp and currently I try to develope 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 develope:



enter image description here





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 itselfs 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 that 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:

1. 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?


2. 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 wether 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.










share|improve this question









$endgroup$

















    0












    $begingroup$


    I am developing another bigger project, I want to try a simple DatingApp and currently I try to develope 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 develope:



    enter image description here





    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 itselfs 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 that 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:

    1. 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?


    2. 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 wether 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.










    share|improve this question









    $endgroup$















      0












      0








      0





      $begingroup$


      I am developing another bigger project, I want to try a simple DatingApp and currently I try to develope 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 develope:



      enter image description here





      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 itselfs 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 that 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:

      1. 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?


      2. 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 wether 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.










      share|improve this question









      $endgroup$




      I am developing another bigger project, I want to try a simple DatingApp and currently I try to develope 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 develope:



      enter image description here





      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 itselfs 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 that 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:

      1. 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?


      2. 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 wether 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 user-interface firebase






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 39 mins ago









      J. LoJ. Lo

      62




      62






















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


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215717%2fcodereview-of-a-firebase-login-registration-process%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
















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215717%2fcodereview-of-a-firebase-login-registration-process%23new-answer', 'question_page');
          }
          );

          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







          Popular posts from this blog

          Список кардиналов, возведённых папой римским Каликстом III

          Deduzione

          Mysql.sock missing - “Can't connect to local MySQL server through socket”