package com.airlocksoftware.hackernews.activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import com.airlocksoftware.hackernews.R;
import com.airlocksoftware.hackernews.data.UserPrefs;
import com.airlocksoftware.hackernews.loader.LoginLoader;
import com.airlocksoftware.hackernews.model.Comment;
import com.airlocksoftware.hackernews.model.Result;
import com.airlocksoftware.hackernews.model.Story;
import com.airlocksoftware.holo.utils.ViewUtils;
/**
* Activity for logging in to news.ycombinator.com. The user will be forwarded to this activity if they try to perform a
* operation that requires authentication while not logged in. The action the user was attempting to perform is passed
* in via the extras bundle as a PostAction, which is then performed automatically once the user is authenticated.
*
* Currently username & passwords are stored in SharedPreferences, which seems to be ok as per [1]. It doesn't make
* much sense to encrypt the data when the encryption algorithm would be client side and the source would be available.
* [1]: http://stackoverflow.com/questions/785973/what-is-the-most-appropriate-way-to-store-user-settings-in-android-
* application
*
* TODO Add a checkbox for "remember my password" and only save credentials if checked
**/
public class LoginActivity extends SlideoutMenuActivity implements LoaderCallbacks<Result> {
private String mUsername = null;
private String mPassword = null;
private PostAction mPostAction;
// used for post-actions VOTE & REPLY
private Story mStory;
private Comment mComment;
// use for post-actions SUBMIT
private String mSubTitle;
private String mSubText;
private EditText mEditUsername;
private EditText mEditPassword;
private View mLoginButton;
private View mLoginIndicator;
private View.OnClickListener mSubmitBtnListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
performSubmit();
}
};
private OnEditorActionListener mSubmitKeyboardListener = new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
performSubmit();
// hide keyboard
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null) {
inputManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
return true;
} else return false;
}
};
public enum PostAction {
UPVOTE, REPLY, SUBMIT;
}
public enum LoginResult {
EMPTY, SUCCESS, FAILURE;
}
// CONSTANTS
private static final String TAG = LoginActivity.class.getSimpleName();
public static final String POST_ACTION = TAG + ".postAction";
public static final String POST_STORY = TAG + ".postStory";
public static final String POST_COMMENT = TAG + ".postComment";
public static final String POST_SUB_TITLE = TAG + ".postSubTitle";
public static final String POST_SUB_TEXT = TAG + ".postSubText";
public static final String USERNAME = TAG + ".username";
public static final String PASSWORD = TAG + ".password";
// ACTIVITY LIFECYCLE
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.act_login);
ViewUtils.fixBackgroundRepeat(findViewById(R.id.root_act_login));
findViews();
retrieveBundles(savedState, getIntent().getExtras());
// get settings from SharedPrefs
UserPrefs userData = new UserPrefs(this);
if (userData.isLoggedIn()) {
Toast t = Toast.makeText(this, "Already logged in", Toast.LENGTH_SHORT);
t.show();
finish();
}
// setup UI
getActionBarView().getController().setTitleText(getString(R.string.login));
setActiveMenuItem(-1); // clear check
showContent();
// start loader
getSupportLoaderManager().initLoader(0, null, this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (mEditUsername.getText() != null) mUsername = mEditUsername.getText().toString();
if (mEditPassword.getText() != null) mPassword = mEditPassword.getText().toString();
if (mPostAction != null) outState.putSerializable(POST_ACTION, mPostAction);
if (mStory != null) outState.putSerializable(POST_STORY, mStory);
if (mComment != null) outState.putSerializable(POST_COMMENT, mComment);
if (mSubTitle != null) outState.putSerializable(POST_SUB_TITLE, mSubTitle);
if (mSubText != null) outState.putSerializable(POST_SUB_TEXT, mSubText);
if (mUsername != null) outState.putString(USERNAME, mUsername);
if (mPassword != null) outState.putString(PASSWORD, mPassword);
super.onSaveInstanceState(outState);
}
@Override
public void finish() {
super.finish();
// TODO need to restart other activities in back stack if we've logged in
}
// Loader callbacks
@Override
public Loader<Result> onCreateLoader(int id, Bundle args) {
return new LoginLoader(this, mUsername, mPassword);
}
@Override
public void onLoadFinished(Loader<Result> loader, Result result) {
if (result == Result.EMPTY) {
return; // this means the request was from initLoader()
}
if (result == Result.SUCCESS) {
Toast t = Toast.makeText(this, getString(R.string.login_success), Toast.LENGTH_SHORT);
t.show();
doPostAction();
finish();
} else {
showError();
Toast t = Toast.makeText(this, getString(R.string.login_failure), Toast.LENGTH_LONG);
t.show();
}
}
private void doPostAction() {
if (mPostAction == null) return;
switch (mPostAction) {
case UPVOTE:
if (mComment != null) mComment.upvote(this);
else if (mStory != null) mStory.upvote(this);
break;
case REPLY:
if (mComment != null) ReplyActivity.startCommentReplyActivity(this, mComment);
else if (mStory != null) ReplyActivity.startStoryReplyActivity(this, mStory);
break;
case SUBMIT:
SubmitActivity.startSubmitActivity(this, mSubTitle, mSubText);
break;
}
}
@Override
public void onLoaderReset(Loader<Result> loader) {
// No implementation necessary
}
// private methods
protected void performSubmit() {
if (mEditUsername.getText() != null) mUsername = mEditUsername.getText().toString();
if (mEditPassword.getText() != null) mPassword = mEditPassword.getText().toString();
showLoading();
getSupportLoaderManager().restartLoader(0, null, LoginActivity.this);
}
private void findViews() {
mEditUsername = (EditText) findViewById(R.id.edit_username);
mEditPassword = (EditText) findViewById(R.id.edit_password);
mLoginButton = findViewById(R.id.btn_login);
mLoginIndicator = findViewById(R.id.login_indicator);
mLoginButton.setOnClickListener(mSubmitBtnListener);
mEditPassword.setOnEditorActionListener(mSubmitKeyboardListener);
}
private void retrieveBundles(Bundle savedState, Bundle extras) {
if (savedState != null) {
mPostAction = (PostAction) savedState.getSerializable(POST_ACTION);
mStory = (Story) savedState.getSerializable(POST_STORY);
mComment = (Comment) savedState.getSerializable(POST_COMMENT);
mSubTitle = savedState.getString(POST_SUB_TITLE);
mSubText = savedState.getString(POST_SUB_TEXT);
mUsername = savedState.getString(USERNAME);
mPassword = savedState.getString(PASSWORD);
}
if (extras != null) {
mPostAction = (PostAction) extras.getSerializable(POST_ACTION);
mStory = (Story) extras.getSerializable(POST_STORY);
mComment = (Comment) extras.getSerializable(POST_COMMENT);
mSubTitle = extras.getString(POST_SUB_TITLE);
mSubText = extras.getString(POST_SUB_TEXT);
}
}
private void showLoading() {
if (mEditUsername != null) mEditUsername.setEnabled(false);
if (mEditPassword != null) mEditPassword.setEnabled(false);
mLoginButton.setVisibility(View.GONE);
mLoginIndicator.setVisibility(View.VISIBLE);
}
private void showContent() {
if (mEditUsername != null) mEditUsername.setEnabled(true);
if (mEditPassword != null) mEditPassword.setEnabled(true);
mLoginButton.setVisibility(View.VISIBLE);
mLoginIndicator.setVisibility(View.GONE);
}
private void showError() {
if (mEditUsername != null) mEditUsername.setEnabled(true);
if (mEditPassword != null) mEditPassword.setEnabled(true);
mLoginButton.setVisibility(View.VISIBLE);
mLoginIndicator.setVisibility(View.GONE);
}
}