pt.up.mobile.authenticator.AuthenticatorActivity.java Source code

Java tutorial

Introduction

Here is the source code for pt.up.mobile.authenticator.AuthenticatorActivity.java

Source

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package pt.up.mobile.authenticator;

import pt.up.mobile.AccountAuthenticatorActivity;
import pt.up.mobile.Constants;
import pt.up.mobile.R;
import pt.up.mobile.content.SigarraContract;
import pt.up.mobile.datatypes.User;
import pt.up.mobile.sifeup.AuthenticationUtils;
import pt.up.mobile.sifeup.ResponseCommand;
import pt.up.mobile.syncadapter.SigarraSyncAdapterUtils;
import pt.up.mobile.ui.dialogs.AboutDialogFragment;
import pt.up.mobile.ui.dialogs.ProgressDialogFragment;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.TextView;

/**
 * Activity which displays login screen to the user.
 */
public class AuthenticatorActivity extends AccountAuthenticatorActivity
        implements ResponseCommand<User>, OnDismissListener {

    private static final String DIALOG_AUTHENTICATING = "connecting";
    private static final String DIALOG_ABOUT = "about";
    /** The Intent flag to confirm credentials. */
    public static final String PARAM_CONFIRM_CREDENTIALS = "confirmCredentials";

    /** The Intent extra to store password. */
    public static final String PARAM_PASSWORD = "password";

    /** The Intent extra to store username. */
    public static final String PARAM_USERNAME = "username";

    /** The Intent extra to store username. */
    public static final String PARAM_AUTHTOKEN_TYPE = "authtokenType";

    /** The tag used to log to adb console. */
    private static final String TAG = "AuthenticatorActivity";
    private AccountManager mAccountManager;

    /** Keep track of the login task so can cancel it if requested */
    private AsyncTask<String, Void, ERROR_TYPE> mAuthTask = null;

    /**
     * If set we are just checking that the user knows their credentials; this
     * doesn't cause the user's password or authToken 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) {

        Log.i(TAG, "onCreate(" + icicle + ")");
        super.onCreate(icicle);
        setContentView(R.layout.login);
        mMessage = (TextView) findViewById(R.id.message);
        mUsernameEdit = (EditText) findViewById(R.id.login_username);
        mPasswordEdit = (EditText) findViewById(R.id.login_pass);
        mAccountManager = AccountManager.get(this);
        Log.i(TAG, "loading data from Intent");
        final Intent intent = getIntent();
        mUsername = intent.getStringExtra(PARAM_USERNAME);
        mRequestNewAccount = mUsername == null;
        mConfirmCredentials = intent.getBooleanExtra(PARAM_CONFIRM_CREDENTIALS, false);
        if (mConfirmCredentials) {
            mUsernameEdit.setKeyListener(null); // disable username editing
            mPasswordEdit.requestFocus();
        }
        Log.i(TAG, "    request new: " + mRequestNewAccount);

        final CheckBox showPassword = (CheckBox) findViewById(R.id.show_password);
        showPassword.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                final int selection = mPasswordEdit.getSelectionStart();
                if (isChecked) {
                    mPasswordEdit.setTransformationMethod(null);
                } else {
                    mPasswordEdit.setTransformationMethod(new PasswordTransformationMethod());
                }
                mPasswordEdit.setSelection(selection);
            }
        });
        if (!TextUtils.isEmpty(mUsername))
            mUsernameEdit.setText(mUsername);
        mMessage.setText(R.string.login_activity_newaccount_text);
    }

    public void showAbout(View view) {
        AboutDialogFragment.newInstance().show(getSupportFragmentManager(), DIALOG_ABOUT);
    }

    /**
     * Handles onClick event on the Submit button. Sends username/password to
     * the server for authentication. The button is configured to call
     * handleLogin() in the layout XML.
     * 
     * @param view
     *            The Submit button for which this method is invoked
     */
    public void handleLogin(View view) {
        if (mRequestNewAccount) {
            mUsername = mUsernameEdit.getText().toString();
        }
        mPassword = mPasswordEdit.getText().toString();
        if (TextUtils.isEmpty(mUsername) || TextUtils.isEmpty(mPassword)) {
            mMessage.setText(getMessage());
        } else {
            // Show a progress dialog, and kick off a background task to perform
            // the user login attempt.
            showProgress();

            mAuthTask = AuthenticationUtils.authenticate(mUsername, mPassword, this, this);
        }
    }

    /**
     * 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 result
     *            the confirmCredentials result.
     */
    private 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_ACCOUNT_NAME, mUsername);
        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. We store the
     * authToken that's returned from the server as the 'password' for this
     * account - so we're never storing the user's actual password locally.
     * 
     * @param result
     *            the confirmCredentials result.
     */
    @TargetApi(8)
    private void finishLogin(final User user) {

        Log.i(TAG, "finishLogin()");
        new Thread(new Runnable() {
            @Override
            public void run() {
                final Account account = new Account(mUsername, Constants.ACCOUNT_TYPE);
                if (mRequestNewAccount) {
                    final ContentValues values = new ContentValues();
                    values.put(SigarraContract.Users.CODE, user.getUserCode());
                    values.put(SigarraContract.Users.TYPE, user.getType());
                    values.put(SigarraContract.Users.ID, account.name);
                    getContentResolver().insert(SigarraContract.Users.CONTENT_URI, values);
                    // Set contacts sync for this account.

                    if (!mAccountManager.addAccountExplicitly(account, mPassword, Bundle.EMPTY)) {
                        getContentResolver().delete(SigarraContract.Users.CONTENT_URI,
                                SigarraContract.Users.PROFILE,
                                SigarraContract.Users.getUserSelectionArgs(account.name));
                        finish();
                    }
                    String syncIntervalValue = PreferenceManager
                            .getDefaultSharedPreferences(getApplicationContext())
                            .getString(getString(R.string.key_sync_interval),
                                    Integer.toString(getResources().getInteger(R.integer.default_sync_interval)));

                    String syncNotIntervalValue = PreferenceManager
                            .getDefaultSharedPreferences(getApplicationContext())
                            .getString(getString(R.string.key_notifications_sync_interval),
                                    Integer.toString(getResources().getInteger(R.integer.default_sync_interval)));

                    ContentResolver.setSyncAutomatically(account, SigarraContract.CONTENT_AUTHORITY, true);
                    ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
                        ContentResolver.addPeriodicSync(account, SigarraContract.CONTENT_AUTHORITY, Bundle.EMPTY,
                                Integer.parseInt(syncIntervalValue) * 3600);
                        ContentResolver.addPeriodicSync(account, SigarraContract.CONTENT_AUTHORITY,
                                SigarraSyncAdapterUtils.getNotificationsPeriodicBundle(),
                                Integer.parseInt(syncNotIntervalValue) * 3600);
                    } else {
                        PeriodicSyncReceiver.cancelPreviousAlarms(getApplicationContext(), account,
                                SigarraContract.CONTENT_AUTHORITY, Bundle.EMPTY);
                        PeriodicSyncReceiver.addPeriodicSync(getApplicationContext(), account,
                                SigarraContract.CONTENT_AUTHORITY, Bundle.EMPTY,
                                Integer.parseInt(syncIntervalValue) * 3600);

                        PeriodicSyncReceiver.addPeriodicSync(getApplicationContext(), account,
                                SigarraContract.CONTENT_AUTHORITY, SigarraSyncAdapterUtils.getNotificationsBundle(),
                                Integer.parseInt(syncNotIntervalValue) * 3600);
                    }
                } else {
                    mAccountManager.setPassword(account, user.getPassword());
                }
                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();
            }
        }).start();
    }

    /**
     * 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.
            final CharSequence msg = getText(R.string.toast_login_error_empty_username);
            return msg;
        }
        if (TextUtils.isEmpty(mPassword)) {
            // We have an account but no password
            return getText(R.string.toast_login_error_empty_password);
        }
        return null;
    }

    /**
     * Shows the progress UI for a lengthy operation.
     */
    private void showProgress() {
        ProgressDialogFragment.newInstance(getString(R.string.lb_login_authenticating), true)
                .show(getSupportFragmentManager(), DIALOG_AUTHENTICATING);

    }

    /**
     * Hides the progress UI for a lengthy operation.
     */
    private void hideProgress() {
        removeDialog(DIALOG_AUTHENTICATING);
    }

    public void onError(ERROR_TYPE error) {
        hideProgress();
        // Our task is complete, so clear it out
        mAuthTask = null;
        switch (error) {
        case CANCELLED:
            break;
        case AUTHENTICATION:
            mMessage.setText(R.string.toast_login_error_wrong_password);
            break;
        case NETWORK:
            mMessage.setText(R.string.toast_server_error);
            break;
        default:
            mMessage.setText(R.string.general_error);
            break;
        }
    }

    public void onResultReceived(User user) {
        hideProgress();
        if (!mConfirmCredentials) {
            finishLogin(user);
        } else {
            finishConfirmCredentials(true);
        }
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        Log.i(TAG, "user cancelling authentication");
        if (mAuthTask != null) {
            mAuthTask.cancel(true);
        }
    }

    protected void removeDialog(String dialog) {
        // DialogFragment.show() will take care of adding the fragment
        // in a transaction. We also want to remove any currently showing
        // dialog, so make our own transaction and take care of that here.
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment prev = getSupportFragmentManager().findFragmentByTag(dialog);
        if (prev != null) {
            ft.remove(prev).commitAllowingStateLoss();
        }
    }

}