From 02f658cf35c5432ace2d3d23c59ae2cdb0b3ad6c Mon Sep 17 00:00:00 2001 From: Francisco Anzueto Date: Wed, 5 Apr 2017 00:20:13 -0600 Subject: [PATCH 1/3] Implement mvp --- .idea/misc.xml | 2 +- .idea/vcs.xml | 6 + .../java/edu/galileo/mvp/LoginActivity.java | 132 +++--------------- .../main/java/edu/galileo/mvp/LoginModel.java | 15 ++ .../java/edu/galileo/mvp/LoginModelImpl.java | 71 ++++++++++ .../java/edu/galileo/mvp/LoginPresenter.java | 6 + .../edu/galileo/mvp/LoginPresenterImpl.java | 62 ++++++++ .../main/java/edu/galileo/mvp/LoginView.java | 12 ++ 8 files changed, 192 insertions(+), 114 deletions(-) create mode 100644 .idea/vcs.xml create mode 100644 app/src/main/java/edu/galileo/mvp/LoginModel.java create mode 100644 app/src/main/java/edu/galileo/mvp/LoginModelImpl.java create mode 100644 app/src/main/java/edu/galileo/mvp/LoginPresenter.java create mode 100644 app/src/main/java/edu/galileo/mvp/LoginPresenterImpl.java create mode 100644 app/src/main/java/edu/galileo/mvp/LoginView.java diff --git a/.idea/misc.xml b/.idea/misc.xml index 6b8fbd7..4ca3650 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -44,7 +44,7 @@ - + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/edu/galileo/mvp/LoginActivity.java b/app/src/main/java/edu/galileo/mvp/LoginActivity.java index 7fcd8cc..704dd78 100644 --- a/app/src/main/java/edu/galileo/mvp/LoginActivity.java +++ b/app/src/main/java/edu/galileo/mvp/LoginActivity.java @@ -2,10 +2,8 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; -import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.AutoCompleteTextView; @@ -16,19 +14,9 @@ /** * A login screen that offers login via email/password. */ -public class LoginActivity extends AppCompatActivity { +public class LoginActivity extends AppCompatActivity implements LoginView { - /** - * A dummy authentication store containing known user names and passwords. - * TODO: remove after connecting to a real authentication system. - */ - private static final String[] DUMMY_CREDENTIALS = new String[] { - "test@galileo.edu", "testtest" - }; - /** - * Keep track of the login task to ensure we can cancel it if requested. - */ - private UserLoginTask mAuthTask = null; + private LoginPresenter loginPresenter; // UI references. private AutoCompleteTextView mEmailView; @@ -56,6 +44,8 @@ public void onClick(View view) { mLoginFormView = findViewById(R.id.login_form); mProgressView = findViewById(R.id.login_progress); + + loginPresenter = new LoginPresenterImpl(this); } /** @@ -64,10 +54,6 @@ public void onClick(View view) { * errors are presented and no actual login attempt is made. */ private void attemptLogin() { - if (mAuthTask != null) { - return; - } - // Reset errors. mEmailView.setError(null); mPasswordView.setError(null); @@ -76,53 +62,13 @@ private void attemptLogin() { String email = mEmailView.getText().toString(); String password = mPasswordView.getText().toString(); - boolean cancel = false; - View focusView = null; - - // Check for a valid password, if the user entered one. - if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { - mPasswordView.setError(getString(R.string.error_invalid_password)); - focusView = mPasswordView; - cancel = true; - } - - // Check for a valid email address. - if (TextUtils.isEmpty(email)) { - mEmailView.setError(getString(R.string.error_field_required)); - focusView = mEmailView; - cancel = true; - } else if (!isEmailValid(email)) { - mEmailView.setError(getString(R.string.error_invalid_email)); - focusView = mEmailView; - cancel = true; - } - - if (cancel) { - // There was an error; don't attempt login and focus the first - // form field with an error. - focusView.requestFocus(); - } else { - // Show a progress spinner, and kick off a background task to - // perform the user login attempt. - showProgress(true); - mAuthTask = new UserLoginTask(email, password); - mAuthTask.execute((Void) null); - } - } - - private boolean isEmailValid(String email) { - //TODO: Replace this with your own logic - return email.contains("@"); - } - - private boolean isPasswordValid(String password) { - //TODO: Replace this with your own logic - return password.length() > 4; + loginPresenter.validateCredentials(email, password); } /** * Shows the progress UI and hides the login form. */ + @Override public void showProgress(final boolean show) { int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); @@ -147,61 +93,21 @@ public void onAnimationEnd(Animator animation) { }); } - /** - * Represents an asynchronous login/registration task used to authenticate - * the user. - */ - public class UserLoginTask extends AsyncTask { - - private final String mEmail; - private final String mPassword; - - UserLoginTask(String email, String password) { - mEmail = email; - mPassword = password; - } - - @Override - protected Boolean doInBackground(Void... params) { - // TODO: attempt authentication against a network service. - - try { - // Simulate network access. - Thread.sleep(2000); - } catch (InterruptedException e) { - return false; - } - - for (String credential : DUMMY_CREDENTIALS) { - String[] pieces = credential.split(":"); - if (pieces[0].equals(mEmail)) { - // Account exists, return true if the password matches. - return pieces[1].equals(mPassword); - } - } - - // TODO: register the new account here. - return true; - } - - @Override - protected void onPostExecute(final Boolean success) { - mAuthTask = null; - showProgress(false); + @Override + public void setUsernameError(int messageResId) { + mEmailView.setError(getString(messageResId)); + mEmailView.requestFocus(); + } - if (success) { - Toast.makeText(LoginActivity.this, "Success", Toast.LENGTH_SHORT).show(); - } else { - mPasswordView.setError(getString(R.string.error_incorrect_password)); - mPasswordView.requestFocus(); - } - } + @Override + public void setPasswordError(int messageResId) { + mPasswordView.setError(getString(messageResId)); + mPasswordView.requestFocus(); + } - @Override - protected void onCancelled() { - mAuthTask = null; - showProgress(false); - } + @Override + public void successAction() { + Toast.makeText(LoginActivity.this, "Success", Toast.LENGTH_SHORT).show(); } } diff --git a/app/src/main/java/edu/galileo/mvp/LoginModel.java b/app/src/main/java/edu/galileo/mvp/LoginModel.java new file mode 100644 index 0000000..0fe01b3 --- /dev/null +++ b/app/src/main/java/edu/galileo/mvp/LoginModel.java @@ -0,0 +1,15 @@ +package edu.galileo.mvp; + +public interface LoginModel { + + interface OnLoginFinishedListener { + + void onCanceled(); + + void onPasswordError(); + + void onSuccess(); + } + + void login(String username, String password, OnLoginFinishedListener listener); +} diff --git a/app/src/main/java/edu/galileo/mvp/LoginModelImpl.java b/app/src/main/java/edu/galileo/mvp/LoginModelImpl.java new file mode 100644 index 0000000..a4e0d23 --- /dev/null +++ b/app/src/main/java/edu/galileo/mvp/LoginModelImpl.java @@ -0,0 +1,71 @@ +package edu.galileo.mvp; + +import android.os.AsyncTask; + +public class LoginModelImpl implements LoginModel { + + private OnLoginFinishedListener listener; + + /** + * A dummy authentication store containing known user names and passwords. + * TODO: remove after connecting to a real authentication system. + */ + private static final String[] DUMMY_CREDENTIALS = new String[] { + "test@galileo.edu:asdfasdf", "test2@galileo.edu:asdfasdf" + }; + + @Override + public void login(String username, String password, OnLoginFinishedListener listener) { + this.listener = listener; + + new UserLoginTask(username, password).execute((Void) null); + } + + public class UserLoginTask extends AsyncTask { + + private final String mEmail; + private final String mPassword; + + UserLoginTask(String email, String password) { + mEmail = email; + mPassword = password; + } + + @Override + protected Boolean doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + try { + // Simulate network access. + Thread.sleep(2000); + } catch (InterruptedException e) { + return false; + } + + for (String credential : DUMMY_CREDENTIALS) { + String[] pieces = credential.split(":"); + if (pieces[0].equals(mEmail)) { + // Account exists, return true if the password matches. + return pieces[1].equals(mPassword); + } + } + + // TODO: register the new account here. + return false; + } + + @Override + protected void onPostExecute(final Boolean success) { + if (success) { + listener.onSuccess(); + } else { + listener.onPasswordError(); + } + } + + @Override + protected void onCancelled() { + listener.onCanceled(); + } + } +} diff --git a/app/src/main/java/edu/galileo/mvp/LoginPresenter.java b/app/src/main/java/edu/galileo/mvp/LoginPresenter.java new file mode 100644 index 0000000..80caba8 --- /dev/null +++ b/app/src/main/java/edu/galileo/mvp/LoginPresenter.java @@ -0,0 +1,6 @@ +package edu.galileo.mvp; + +public interface LoginPresenter { + + void validateCredentials(String username, String password); +} diff --git a/app/src/main/java/edu/galileo/mvp/LoginPresenterImpl.java b/app/src/main/java/edu/galileo/mvp/LoginPresenterImpl.java new file mode 100644 index 0000000..25bb28e --- /dev/null +++ b/app/src/main/java/edu/galileo/mvp/LoginPresenterImpl.java @@ -0,0 +1,62 @@ +package edu.galileo.mvp; + +import android.text.TextUtils; + +public class LoginPresenterImpl implements LoginPresenter, LoginModel.OnLoginFinishedListener { + + private LoginView loginView; + private LoginModel loginModel; + + public LoginPresenterImpl(LoginView loginView) { + this.loginView = loginView; + this.loginModel = new LoginModelImpl(); + } + + @Override + public void validateCredentials(String username, String password) { + // Check for a valid password, if the user entered one. + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + loginView.setPasswordError(R.string.error_invalid_password); + return; + } + + // Check for a valid username. + if (TextUtils.isEmpty(username)) { + loginView.setUsernameError(R.string.error_field_required); + return; + } else if (!isEmailValid(username)) { + loginView.setUsernameError(R.string.error_invalid_email); + return; + } + + loginView.showProgress(true); + loginModel.login(username, password, this); + } + + @Override + public void onCanceled() { + loginView.showProgress(false); + } + + @Override + public void onPasswordError() { + loginView.showProgress(false); + loginView.setPasswordError(R.string.error_incorrect_password); + } + + @Override + public void onSuccess() { + loginView.showProgress(false); + loginView.successAction(); + } + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return email.contains("@"); + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return password.length() > 4; + } +} diff --git a/app/src/main/java/edu/galileo/mvp/LoginView.java b/app/src/main/java/edu/galileo/mvp/LoginView.java new file mode 100644 index 0000000..ca5c088 --- /dev/null +++ b/app/src/main/java/edu/galileo/mvp/LoginView.java @@ -0,0 +1,12 @@ +package edu.galileo.mvp; + +public interface LoginView { + + void showProgress(boolean showProgress); + + void setUsernameError(int messageResId); + + void setPasswordError(int messageResId); + + void successAction(); +} From a1940d44bf6ba8844eb3ad6795cd7868542d5424 Mon Sep 17 00:00:00 2001 From: Francisco Anzueto Date: Tue, 18 Apr 2017 11:11:20 -0600 Subject: [PATCH 2/3] Use butterknife to bind ui elements and listeners --- app/build.gradle | 2 ++ .../java/edu/galileo/mvp/LoginActivity.java | 34 ++++++------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 220e8fa..2c0de97 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,5 +26,7 @@ dependencies { }) compile 'com.android.support:appcompat-v7:25.3.0' compile 'com.android.support:design:25.3.0' + compile 'com.jakewharton:butterknife:8.5.1' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' testCompile 'junit:junit:4.12' } diff --git a/app/src/main/java/edu/galileo/mvp/LoginActivity.java b/app/src/main/java/edu/galileo/mvp/LoginActivity.java index 704dd78..30303db 100644 --- a/app/src/main/java/edu/galileo/mvp/LoginActivity.java +++ b/app/src/main/java/edu/galileo/mvp/LoginActivity.java @@ -5,12 +5,14 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; -import android.view.View.OnClickListener; import android.widget.AutoCompleteTextView; -import android.widget.Button; import android.widget.EditText; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + /** * A login screen that offers login via email/password. */ @@ -19,31 +21,16 @@ public class LoginActivity extends AppCompatActivity implements LoginView { private LoginPresenter loginPresenter; // UI references. - private AutoCompleteTextView mEmailView; - private EditText mPasswordView; - private View mProgressView; - private View mLoginFormView; + @BindView(R.id.email) AutoCompleteTextView mEmailView; + @BindView(R.id.password) EditText mPasswordView; + @BindView(R.id.login_form) View mProgressView; + @BindView(R.id.login_progress) View mLoginFormView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); - // Set up the login form. - mEmailView = (AutoCompleteTextView) findViewById(R.id.email); - - mPasswordView = (EditText) findViewById(R.id.password); - - Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); - mEmailSignInButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View view) { - attemptLogin(); - } - }); - - mLoginFormView = findViewById(R.id.login_form); - mProgressView = findViewById(R.id.login_progress); + ButterKnife.bind(this); loginPresenter = new LoginPresenterImpl(this); } @@ -53,7 +40,8 @@ public void onClick(View view) { * If there are form errors (invalid email, missing fields, etc.), the * errors are presented and no actual login attempt is made. */ - private void attemptLogin() { + @OnClick(R.id.email_sign_in_button) + public void attemptLogin() { // Reset errors. mEmailView.setError(null); mPasswordView.setError(null); From fa7416323962dc557b670ba7f201a164380904c6 Mon Sep 17 00:00:00 2001 From: Francisco Anzueto Date: Tue, 18 Apr 2017 13:41:40 -0600 Subject: [PATCH 3/3] fix bind references --- app/src/main/java/edu/galileo/mvp/LoginActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/edu/galileo/mvp/LoginActivity.java b/app/src/main/java/edu/galileo/mvp/LoginActivity.java index 30303db..2e0f567 100644 --- a/app/src/main/java/edu/galileo/mvp/LoginActivity.java +++ b/app/src/main/java/edu/galileo/mvp/LoginActivity.java @@ -23,8 +23,8 @@ public class LoginActivity extends AppCompatActivity implements LoginView { // UI references. @BindView(R.id.email) AutoCompleteTextView mEmailView; @BindView(R.id.password) EditText mPasswordView; - @BindView(R.id.login_form) View mProgressView; - @BindView(R.id.login_progress) View mLoginFormView; + @BindView(R.id.login_form) View mLoginFormView; + @BindView(R.id.login_progress) View mProgressView; @Override protected void onCreate(Bundle savedInstanceState) {