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/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 7fcd8cc..2e0f567 100644
--- a/app/src/main/java/edu/galileo/mvp/LoginActivity.java
+++ b/app/src/main/java/edu/galileo/mvp/LoginActivity.java
@@ -2,60 +2,37 @@
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;
-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.
*/
-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;
- 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 mLoginFormView;
+ @BindView(R.id.login_progress) View mProgressView;
@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);
+ ButterKnife.bind(this);
- 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);
+ loginPresenter = new LoginPresenterImpl(this);
}
/**
@@ -63,11 +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() {
- if (mAuthTask != null) {
- return;
- }
-
+ @OnClick(R.id.email_sign_in_button)
+ public void attemptLogin() {
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
@@ -76,53 +50,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 +81,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();
+}