1
\$\begingroup\$

So this is my first time implementing the MVP pattern in Android.

As I understand the presenter should not contain any Android specific code. I am using TextInputLayout and I want to validate my text fields and show an error message using TextinputLayout.setError(""). Now the edittext validation logic is implemented in the LoginActivity but is there any way to separate the logic to the presenter? The code is given below:

This is the LoginActivity where I validate in the username and password in empty in the validate method and pass them to the presenter in the Login method.

LoginActivity(View):

public class LoginActivity extends AppCompatActivity implements LoginView {

//To maintain the orientation changes
private final StateMaintainer mStateMaintainer =
        new StateMaintainer(getSupportFragmentManager(),    LoginActivity.class.getName());
@Inject
public LoginPresenter mPresenter;

@BindView(R.id.res_parent)
RelativeLayout relParent;
@BindView(R.id.txt_inp_username)
TextInputLayout inpUserName;
@BindView(R.id.edt_username)
TextInputEditText edtUsername;
@BindView(R.id.txt_inp_password)
TextInputLayout inpPassword;
@BindView(R.id.edt_password)
TextInputEditText edtPassword;

@OnClick(R.id.btn_login)
public void Login() {
    if (validate()) {
        mPresenter.loginUser(edtUsername.getText().toString(), edtPassword.getText().toString());
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    //getWindow().setBackgroundDrawable(null);
    if (getSupportActionBar() != null) {
        getSupportActionBar().hide();
    }
    ButterKnife.bind(this);
    setupMVP();

}

void setupMVP() {
    if (mStateMaintainer.firstTimeIn()) {
        initialize();
    } else {
        reinitialize();
    }
    /*if (mStateMaintainer.firstTimeIn()) {
        LoginPresenterImpl presenter = new LoginPresenterImpl(this);
        LoginInteractorImpl loginInteractor = new LoginInteractorImpl(presenter);
        presenter.setInteractor(loginInteractor);
        mStateMaintainer.put(presenter);
        mStateMaintainer.put(loginInteractor);
        mPresenter = presenter;
    } else {
        mPresenter = mStateMaintainer.get(LoginPresenterImpl.class.getName());
        // Updated the View in Presenter
        mPresenter.setView(this);
    }*/

}

void initialize() {
    setupComponent();
    mStateMaintainer.put(LoginPresenterImpl.class.getName(), mPresenter);
}

void reinitialize() {
    mPresenter = mStateMaintainer.get(LoginPresenterImpl.class.getName());
    mPresenter.setView(this);
    if (mPresenter == null) {
        setupComponent();
    }
}

private void setupComponent() {
    GenericApp.get(this)
            .component()
            .getLoginComponent(new LoginActivityModule(this))
            .inject(this);
}
//Validate TextInputEditText
private boolean validate() {
    Boolean isValid = true;
    List<IValidation> validation = new ArrayList<>();
    validation.add(new EditTextRequired(this, edtPassword,
            inpPassword, "Password is required"));
    validation.add(new EditTextRequired(this, edtUsername,
            inpUserName, "Username/Email is required"));

    for (IValidation v : validation) {
        if (!v.invoke()) {
            isValid = false;
        }
    }
    return isValid;
}


@Override
public void LoginSuccess(String SuccessMessage) {
    Snackbar.make(relParent, SuccessMessage, Snackbar.LENGTH_LONG).show();
}

@Override
public void LoginFailure(String FailureMessage) {
    Snackbar.make(relParent, FailureMessage, Snackbar.LENGTH_LONG).show();
}

@Override
public Context getAppContext() {
    return getApplicationContext();
}

@Override
public Context getActivityContext() {
    return this;
}

@Override
public void showProgress() {
    Utils.showProgressDialog(this, "");
}

@Override
public void hideProgress() {
    Utils.hideProgessDialog(this);
}

@Override
public void showToast() {

}

@Override
protected void onDestroy() {
    super.onDestroy();
    mPresenter.onDestroy(isChangingConfigurations());
}}

This is the implementation of my LoginPresenter interface. Here the presenter logs in the user using the loginUser method.If possible I want to do the Edittext validation(for empty strings) in this method.

LoginPresenter(Presenter):

public class LoginPresenterImpl implements LoginPresenter {


//private final LoginInteractorImpl mInteractor;
private LoginInteractor mInteractor;
private WeakReference<LoginView> mView;

public LoginPresenterImpl(LoginView view) {
    mView = new WeakReference<>(view);
    //mInteractor = new LoginInteractorImpl(this);
}


private LoginView getView() throws NullPointerException {
    if (mView != null) {
        return mView.get();
    } else {
        throw new NullPointerException("View is not available");
    }
}

public void setInteractor(LoginInteractor interactor){
    mInteractor=interactor;
}

@Override
public void setView(LoginView view) {
    mView = new WeakReference<>(view);
}

@Override
public void loginUser(String username, String password) {
    getView().showProgress();
    mInteractor.attemptLogin(username, password);
}

@Override
public void onDestroy(boolean isChangingConfiguration) {
    mView = null;
}

@Override
public Context getAppContext() {
    try {
        return getView().getAppContext();
    } catch (NullPointerException e) {
        return null;
    }

}

@Override
public Context getActivityContext() {
    try {
        return getView().getActivityContext();
    } catch (NullPointerException e) {
        return null;
    }
}

@Override
public void onSuccess(String SuccessMessage) {
    getView().hideProgress();
    getView().LoginSuccess(SuccessMessage);
}

@Override
public void onFailure(String FailureMessage) {
    getView().hideProgress();
    getView().LoginFailure(FailureMessage);
}}

This is the implementation of LoginInteractor interface. I've used retrofit2 library to make the http call. In the onResponse method it calls the LoginPresenter'sonSuccess("") or onFailure("") depending on server response.

LoginInteractor(Model):

public class LoginInteractorImpl implements LoginInteractor, Callback<LoginResponse> {

private final LoginPresenter mPresenter;

public LoginInteractorImpl(LoginPresenter mPresenter) {
    this.mPresenter = mPresenter;
}

@Override
public void attemptLogin(String username, String password) {
    Map<String, String> loginMap = new HashMap<>();
    loginMap.put("username", username);
    loginMap.put("password", password);
    LoginApi apiInterface = ApiClient.getClient().create(LoginApi.class);
    Call<LoginResponse> call = apiInterface.LoginUser(loginMap);
    call.enqueue(this);
}

@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
    Log.v("Login", "" + response.code());
    if (response.code() == 200) {
        mPresenter.onSuccess("Login Success");
    } else {
        mPresenter.onFailure("Username or password is Incorrect");
    }


}

@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
    Log.v("Login", "" + t.toString());
    mPresenter.onFailure("Failed to login");
}}

This class is used to validate the TextInputEditText and to show the error message in TextInputLayout.setError("").

EditTextRequired:

//Validate edittext and set error
public class EditTextRequired implements IValidation {

Activity context;
TextInputEditText text;
TextInputLayout Error;
String ErrorMessage;

public EditTextRequired(Activity context, TextInputEditText text, TextInputLayout Error, String ErrorMessage) {
    this.context = context;
    this.text = text;
    this.Error = Error;
    this.ErrorMessage = ErrorMessage;
}

@Override
public Boolean invoke() {

    if (text.getText().toString().trim().length() < 1) {
        Error.setError(ErrorMessage);
        requestFocus(text);
        return false;
    }
    Error.setErrorEnabled(false);
    return true;
}

private void requestFocus(View view) {
    if (view.requestFocus()) {
        context.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
    }
}}

Also any feedback on my code will be appreciated i.e is my implementation of the android MVP pattern is correct or not.

\$\endgroup\$
  • \$\begingroup\$ As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does in a more specific manner. Please see How to get the best value out of Code Review - Asking Questions for guidance on writing good question titles. \$\endgroup\$ – Mast Sep 20 '16 at 10:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.