/*
* DeliciousDroid - http://code.google.com/p/DeliciousDroid/
*
* Copyright (C) 2010 Matt Schmidt
*
* DeliciousDroid is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* DeliciousDroid is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DeliciousDroid; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
package com.deliciousdroid.authenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountManager;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.EditText;
import android.widget.TextView;
import com.deliciousdroid.R;
import com.deliciousdroid.Constants;
import com.deliciousdroid.client.NetworkUtilities;
import com.deliciousdroid.providers.BookmarkContentProvider;
import com.deliciousdroid.util.SyncUtils;
/**
* Activity which displays login screen to the user.
*/
public class AuthenticatorActivity extends AccountAuthenticatorActivity {
public static final String PARAM_CONFIRMCREDENTIALS = "confirmCredentials";
public static final String PARAM_PASSWORD = "password";
public static final String PARAM_USERNAME = "username";
public static final String PARAM_AUTHTOKEN_TYPE = "authtokenType";
private static final String TAG = "AuthenticatorActivity";
private AccountManager mAccountManager;
private UserLoginTask mAuthTask = null;
private ProgressDialog mProgressDialog = null;
/**
* If set we are just checking that the user knows their credentials; this
* doesn't cause the user's password to be changed on the device.
*/
private Boolean mConfirmCredentials = false;
private TextView mMessage;
private String mPassword;
private EditText mPasswordEdit;
/** Was the original caller asking for an entirely new account? */
protected boolean mRequestNewAccount = false;
private String mUsername;
private EditText mUsernameEdit;
/**
* {@inheritDoc}
*/
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mAccountManager = AccountManager.get(this);
final Intent intent = getIntent();
mUsername = intent.getStringExtra(PARAM_USERNAME);
mRequestNewAccount = mUsername == null;
mConfirmCredentials = intent.getBooleanExtra(PARAM_CONFIRMCREDENTIALS, false);
requestWindowFeature(Window.FEATURE_LEFT_ICON);
setContentView(R.layout.login_activity);
getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_dialog_alert);
mUsernameEdit = (EditText) findViewById(R.id.username_edit);
mPasswordEdit = (EditText) findViewById(R.id.password_edit);
mMessage = (TextView) findViewById(R.id.message);
if (!TextUtils.isEmpty(mUsername)){
mUsernameEdit.setText(mUsername);
mPasswordEdit.requestFocus();
}
mMessage.setText(getMessage());
}
/*
* {@inheritDoc}
*/
@Override
protected Dialog onCreateDialog(int id) {
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage(getText(R.string.ui_activity_authenticating));
dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
Log.i(TAG, "dialog cancel has been invoked");
if (mAuthTask != null) {
mAuthTask.cancel(true);
finish();
}
}
});
mProgressDialog = dialog;
return dialog;
}
/**
* Handles onClick event on the Submit button. Sends username/password to
* the server for authentication.
*
* @param view The Submit button for which this method is invoked
*/
public void handleLogin(View view) {
if (mRequestNewAccount) {
mUsername = mUsernameEdit.getText().toString().trim();
}
mPassword = mPasswordEdit.getText().toString();
if (TextUtils.isEmpty(mUsername) || TextUtils.isEmpty(mPassword)) {
mMessage.setText(getMessage());
} else {
showProgress();
// Start authenticating...
mAuthTask = new UserLoginTask();
mAuthTask.execute();
}
}
/**
* Called when response is received from the server for confirm credentials
* request. See onAuthenticationResult(). Sets the
* AccountAuthenticatorResult which is sent back to the caller.
*
* @param the confirmCredentials result.
*/
protected void finishConfirmCredentials(boolean result) {
Log.i(TAG, "finishConfirmCredentials()");
final Account account = new Account(mUsername, Constants.ACCOUNT_TYPE);
mAccountManager.setPassword(account, mPassword);
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_BOOLEAN_RESULT, result);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();
}
/**
*
* Called when response is received from the server for authentication
* request. See onAuthenticationResult(). Sets the
* AccountAuthenticatorResult which is sent back to the caller. Also sets
* the authToken in AccountManager for this account.
*
* @param the confirmCredentials result.
*/
protected void finishLogin() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
final int synctime = Integer.parseInt(settings.getString("pref_synctime", "0"));
final Account account = new Account(mUsername, Constants.ACCOUNT_TYPE);
if (mRequestNewAccount) {
mAccountManager.addAccountExplicitly(account, mPassword, null);
ContentResolver.setSyncAutomatically(account, BookmarkContentProvider.AUTHORITY, true);
if(synctime != 0) {
SyncUtils.addPeriodicSync(BookmarkContentProvider.AUTHORITY, Bundle.EMPTY, synctime, this);
}
} else {
mAccountManager.setPassword(account, mPassword);
}
final Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mUsername);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();
}
/**
* Called when the authentication process completes (see attemptLogin()).
*/
public void onAuthenticationResult(boolean result) {
mAuthTask = null;
hideProgress();
if (result) {
if (!mConfirmCredentials) {
finishLogin();
} else {
finishConfirmCredentials(result);
}
} else {
Log.e(TAG, "onAuthenticationResult: failed to authenticate");
if (mRequestNewAccount) {
// "Please enter a valid username/password.
mMessage.setText(getText(R.string.login_activity_loginfail_text_both));
} else {
// "Please enter a valid password." (Used when the
// account is already in the database but the password
// doesn't work.)
mMessage.setText(getText(R.string.login_activity_loginfail_text_pwonly));
}
}
}
/**
* Returns the message to be displayed at the top of the login dialog box.
*/
private CharSequence getMessage() {
if (TextUtils.isEmpty(mUsername)) {
// If no username, then we ask the user to log in using an
// appropriate service.
return getText(R.string.login_activity_newaccount_text);
}
if (TextUtils.isEmpty(mPassword)) {
// We have an account but no password
return getText(R.string.login_activity_loginfail_text_pwmissing);
}
return null;
}
/**
* Shows the progress UI for a lengthy operation.
*/
protected void showProgress() {
showDialog(0);
}
/**
* Hides the progress UI for a lengthy operation.
*/
private void hideProgress() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
mProgressDialog = null;
}
}
public void onAuthenticationCancel() {
mAuthTask = null;
hideProgress();
}
/**
* Represents an asynchronous task used to authenticate a user against the
* SampleSync Service
*/
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... params) {
// We do the actual work of authenticating the user
// in the NetworkUtilities class.
try {
return NetworkUtilities.pinboardAuthenticate(mUsername, mPassword);
} catch (Exception ex) {
Log.e(TAG, "UserLoginTask.doInBackground: failed to authenticate");
Log.i(TAG, ex.toString());
return false;
}
}
@Override
protected void onPostExecute(final Boolean success) {
// On a successful authentication, call back into the Activity to
// communicate the authToken (or null for an error).
onAuthenticationResult(success);
}
@Override
protected void onCancelled() {
// If the action was canceled (by the user clicking the cancel
// button in the progress dialog), then call back into the
// activity to let it know.
onAuthenticationCancel();
}
}
}