package com.seafile.seadroid2.account.ui;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.NavUtils;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.seafile.seadroid2.R;
import com.seafile.seadroid2.SeafConnection;
import com.seafile.seadroid2.SeafException;
import com.seafile.seadroid2.account.Account;
import com.seafile.seadroid2.account.AccountInfo;
import com.seafile.seadroid2.account.Authenticator;
import com.seafile.seadroid2.data.DataManager;
import com.seafile.seadroid2.ssl.CertsManager;
import com.seafile.seadroid2.ui.EmailAutoCompleteTextView;
import com.seafile.seadroid2.ui.activity.AccountsActivity;
import com.seafile.seadroid2.ui.activity.BaseActivity;
import com.seafile.seadroid2.ui.dialog.SslConfirmDialog;
import com.seafile.seadroid2.util.ConcurrentAsyncTask;
import com.seafile.seadroid2.util.Utils;
import org.json.JSONException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
public class AccountDetailActivity extends BaseActivity implements Toolbar.OnMenuItemClickListener {
private static final String DEBUG_TAG = "AccountDetailActivity";
private static final String HTTP_PREFIX = "http://";
private static final String HTTPS_PREFIX = "https://";
private TextView statusView;
private Button loginButton;
private EditText serverText;
private ProgressDialog progressDialog;
private EmailAutoCompleteTextView emailText;
private EditText passwdText;
private CheckBox httpsCheckBox;
private TextView seahubUrlHintText;
private ImageView clearEmail, clearPasswd, ivEyeClick;
private RelativeLayout rlEye;
private TextInputLayout authTokenLayout;
private EditText authTokenText;
private android.accounts.AccountManager mAccountManager;
private boolean serverTextHasFocus;
private boolean isPasswddVisible;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_detail);
mAccountManager = android.accounts.AccountManager.get(getBaseContext());
statusView = (TextView) findViewById(R.id.status_view);
loginButton = (Button) findViewById(R.id.login_button);
httpsCheckBox = (CheckBox) findViewById(R.id.https_checkbox);
serverText = (EditText) findViewById(R.id.server_url);
emailText = (EmailAutoCompleteTextView) findViewById(R.id.email_address);
passwdText = (EditText) findViewById(R.id.password);
seahubUrlHintText = (TextView) findViewById(R.id.seahub_url_hint);
clearEmail = (ImageView) findViewById(R.id.iv_delete_email);
clearPasswd = (ImageView) findViewById(R.id.iv_delete_pwd);
rlEye = (RelativeLayout) findViewById(R.id.rl_layout_eye);
ivEyeClick = (ImageView) findViewById(R.id.iv_eye_click);
authTokenLayout = (TextInputLayout) findViewById(R.id.auth_token_hint);
authTokenText = (EditText) findViewById(R.id.auth_token);
authTokenLayout.setVisibility(View.GONE);
setupServerText();
Intent intent = getIntent();
String defaultServerUri = intent.getStringExtra(SeafileAuthenticatorActivity.ARG_SERVER_URI);
if (intent.getBooleanExtra("isEdited", false)) {
String account_name = intent.getStringExtra(SeafileAuthenticatorActivity.ARG_ACCOUNT_NAME);
String account_type = intent.getStringExtra(SeafileAuthenticatorActivity.ARG_ACCOUNT_TYPE);
android.accounts.Account account = new android.accounts.Account(account_name, account_type);
String server = mAccountManager.getUserData(account, Authenticator.KEY_SERVER_URI);
String email = mAccountManager.getUserData(account, Authenticator.KEY_EMAIL);
// isFromEdit = mAccountManager.getUserData(account, Authenticator.KEY_EMAIL);
if (server.startsWith(HTTPS_PREFIX))
httpsCheckBox.setChecked(true);
serverText.setText(server);
emailText.setText(email);
emailText.requestFocus();
seahubUrlHintText.setVisibility(View.GONE);
} else if (defaultServerUri != null) {
if (defaultServerUri.startsWith(HTTPS_PREFIX))
httpsCheckBox.setChecked(true);
serverText.setText(defaultServerUri);
emailText.requestFocus();
} else {
serverText.setText(HTTP_PREFIX);
int prefixLen = HTTP_PREFIX.length();
serverText.setSelection(prefixLen, prefixLen);
}
Toolbar toolbar = getActionBarToolbar();
toolbar.setOnMenuItemClickListener(this);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle(R.string.login);
initListener();
}
private void initListener() {
emailText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus && emailText.getText().toString().trim().length() > 0) {
clearEmail.setVisibility(View.VISIBLE);
} else {
clearEmail.setVisibility(View.INVISIBLE);
}
}
});
passwdText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus && passwdText.getText().toString().trim().length() > 0) {
clearPasswd.setVisibility(View.VISIBLE);
} else {
clearPasswd.setVisibility(View.INVISIBLE);
}
}
});
emailText.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) {
if (emailText.getText().toString().trim().length() > 0) {
clearEmail.setVisibility(View.VISIBLE);
} else {
clearEmail.setVisibility(View.INVISIBLE);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
passwdText.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) {
if (passwdText.getText().toString().trim().length() > 0) {
clearPasswd.setVisibility(View.VISIBLE);
} else {
clearPasswd.setVisibility(View.INVISIBLE);
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
clearEmail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
emailText.setText("");
}
});
clearPasswd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
passwdText.setText("");
}
});
rlEye.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isPasswddVisible) {
ivEyeClick.setImageResource(R.drawable.icon_eye_open);
passwdText.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
} else {
ivEyeClick.setImageResource(R.drawable.icon_eye_close);
passwdText.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
isPasswddVisible = !isPasswddVisible;
passwdText.postInvalidate();
String input = passwdText.getText().toString().trim();
if (!TextUtils.isEmpty(input)) {
passwdText.setSelection(input.length());
}
}
});
}
@Override
protected void onDestroy() {
if (progressDialog != null)
progressDialog.dismiss();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putString("email", emailText.getText().toString());
savedInstanceState.putString("password", passwdText.getText().toString());
super.onSaveInstanceState(savedInstanceState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
emailText.setText((String) savedInstanceState.get("email"));
passwdText.setText((String) savedInstanceState.get("password"));
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
/* FYI {@link http://stackoverflow.com/questions/13293772/how-to-navigate-up-to-the-same-parent-state?rq=1} */
Intent upIntent = new Intent(this, AccountsActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is NOT part of this app's task, so create a new task
// when navigating up, with a synthesized back stack.
TaskStackBuilder.create(this)
// Add all of this activity's parents to the back stack
.addNextIntentWithParentStack(upIntent)
// Navigate up to the closest parent
.startActivities();
} else {
// This activity is part of this app's task, so simply
// navigate up to the logical parent activity.
// NavUtils.navigateUpTo(this, upIntent);
upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(upIntent);
finish();
}
return true;
}
return super.onOptionsItemSelected(item);
}
public void onHttpsCheckboxClicked(View view) {
refreshServerUrlPrefix();
}
private void refreshServerUrlPrefix() {
boolean isHttps = httpsCheckBox.isChecked();
String url = serverText.getText().toString();
String prefix = isHttps ? HTTPS_PREFIX : HTTP_PREFIX;
String urlWithoutScheme = url.replace(HTTPS_PREFIX, "").replace(HTTP_PREFIX, "");
int oldOffset = serverText.getSelectionStart();
// Change the text
serverText.setText(prefix + urlWithoutScheme);
if (serverTextHasFocus) {
// Change the cursor position since we changed the text
if (isHttps) {
int offset = oldOffset + 1;
serverText.setSelection(offset, offset);
} else {
int offset = Math.max(0, oldOffset - 1);
serverText.setSelection(offset, offset);
}
}
}
private void setupServerText() {
serverText.setOnFocusChangeListener(new View.OnFocusChangeListener () {
@Override
public void onFocusChange(View v, boolean hasFocus) {
Log.d(DEBUG_TAG, "serverText has focus: " + (hasFocus ? "yes" : "no"));
serverTextHasFocus = hasFocus;
}
});
serverText.addTextChangedListener(new TextWatcher() {
private String old;
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
old = serverText.getText().toString();
}
@Override
public void afterTextChanged(Editable s) {
// Don't allow the user to edit the "https://" or "http://" part of the serverText
String url = serverText.getText().toString();
boolean isHttps = httpsCheckBox.isChecked();
String prefix = isHttps ? HTTPS_PREFIX : HTTP_PREFIX;
if (!url.startsWith(prefix)) {
int oldOffset = Math.max(prefix.length(), serverText.getSelectionStart());
serverText.setText(old);
serverText.setSelection(oldOffset, oldOffset);
}
}
});
}
/** Called when the user clicks the Login button */
public void login(View view) {
String serverURL = serverText.getText().toString().trim();
String email = emailText.getText().toString().trim();
String passwd = passwdText.getText().toString();
ConnectivityManager connMgr = (ConnectivityManager)
getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) {
if (serverURL.length() == 0) {
statusView.setText(R.string.err_server_andress_empty);
return;
}
if (email.length() == 0) {
emailText.setError(getResources().getString(R.string.err_email_empty));
return;
}
if (passwd.length() == 0) {
passwdText.setError(getResources().getString(R.string.err_passwd_empty));
return;
}
String authToken = null;
if (authTokenLayout.getVisibility() == View.VISIBLE) {
authToken = authTokenText.getText().toString().trim();
if (TextUtils.isEmpty(authToken)) {
authTokenText.setError(getResources().getString(R.string.two_factor_auth_token_empty));
return;
}
}
try {
serverURL = Utils.cleanServerURL(serverURL);
} catch (MalformedURLException e) {
statusView.setText(R.string.invalid_server_address);
Log.d(DEBUG_TAG, "Invalid URL " + serverURL);
return;
}
// force the keyboard to be hidden in all situations
if (getCurrentFocus() != null) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
loginButton.setEnabled(false);
Account tmpAccount = new Account(serverURL, email, null, false);
progressDialog = new ProgressDialog(this);
progressDialog.setMessage(getString(R.string.settings_cuc_loading));
progressDialog.setCancelable(false);
ConcurrentAsyncTask.execute(new LoginTask(tmpAccount, passwd, authToken));
} else {
statusView.setText(R.string.network_down);
}
}
private class LoginTask extends AsyncTask<Void, Void, String> {
Account loginAccount;
SeafException err = null;
String passwd;
String authToken;
public LoginTask(Account loginAccount, String passwd, String authToken) {
this.loginAccount = loginAccount;
this.passwd = passwd;
this.authToken = authToken;
}
@Override
protected void onPreExecute() {
//super.onPreExecute();
progressDialog.show();
}
@Override
protected String doInBackground(Void... params) {
if (params.length != 0)
return "Error number of parameter";
return doLogin();
}
private void resend() {
ConcurrentAsyncTask.execute(new LoginTask(loginAccount, passwd, authToken));
}
@Override
protected void onPostExecute(final String result) {
progressDialog.dismiss();
if (err == SeafException.sslException) {
authTokenLayout.setVisibility(View.GONE);
SslConfirmDialog dialog = new SslConfirmDialog(loginAccount,
new SslConfirmDialog.Listener() {
@Override
public void onAccepted(boolean rememberChoice) {
CertsManager.instance().saveCertForAccount(loginAccount, rememberChoice);
resend();
}
@Override
public void onRejected() {
statusView.setText(result);
loginButton.setEnabled(true);
}
});
dialog.show(getSupportFragmentManager(), SslConfirmDialog.FRAGMENT_TAG);
return;
} else if (err == SeafException.twoFactorAuthTokenMissing) {
// show auth token input box
authTokenLayout.setVisibility(View.VISIBLE);
authTokenText.setError(getString(R.string.two_factor_auth_error));
} else if (err == SeafException.twoFactorAuthTokenInvalid) {
// show auth token input box
authTokenLayout.setVisibility(View.VISIBLE);
authTokenText.setError(getString(R.string.two_factor_auth_invalid));
} else {
authTokenLayout.setVisibility(View.GONE);
}
if (result != null && result.equals("Success")) {
Intent retData = new Intent();
retData.putExtras(getIntent());
retData.putExtra(android.accounts.AccountManager.KEY_ACCOUNT_NAME, loginAccount.getSignature());
retData.putExtra(android.accounts.AccountManager.KEY_AUTHTOKEN, loginAccount.getToken());
retData.putExtra(android.accounts.AccountManager.KEY_ACCOUNT_TYPE, getIntent().getStringExtra(SeafileAuthenticatorActivity.ARG_ACCOUNT_TYPE));
retData.putExtra(SeafileAuthenticatorActivity.ARG_EMAIL, loginAccount.getEmail());
retData.putExtra(SeafileAuthenticatorActivity.ARG_SERVER_URI, loginAccount.getServer());
setResult(RESULT_OK, retData);
finish();
} else {
statusView.setText(result);
}
loginButton.setEnabled(true);
}
private String doLogin() {
SeafConnection sc = new SeafConnection(loginAccount);
try {
// if successful, this will place the auth token into "loginAccount"
if (!sc.doLogin(passwd, authToken))
return getString(R.string.err_login_failed);
// fetch email address from the server
DataManager manager = new DataManager(loginAccount);
AccountInfo accountInfo = manager.getAccountInfo();
if (accountInfo == null)
return "Unknown error";
// replace email address/username given by the user with the address known by the server.
loginAccount = new Account(loginAccount.server, accountInfo.getEmail(), loginAccount.token, false);
return "Success";
} catch (SeafException e) {
err = e;
if (e == SeafException.sslException) {
return getString(R.string.ssl_error);
} else if (e == SeafException.twoFactorAuthTokenMissing) {
return getString(R.string.two_factor_auth_error);
} else if (e == SeafException.twoFactorAuthTokenInvalid) {
return getString(R.string.two_factor_auth_invalid);
}
switch (e.getCode()) {
case HttpURLConnection.HTTP_BAD_REQUEST:
return getString(R.string.err_wrong_user_or_passwd);
case HttpURLConnection.HTTP_NOT_FOUND:
return getString(R.string.invalid_server_address);
default:
return e.getMessage();
}
} catch (JSONException e) {
return e.getMessage();
}
}
}
}