com.xorcode.andtweet.PreferencesActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.xorcode.andtweet.PreferencesActivity.java

Source

/* 
 * Copyright (C) 2008 Torgny Bjers
 * Copyright (C) 2010 Brion N. Emde, "BLOA" example, http://github.com/brione/Brion-Learns-OAuth 
 * Copyright (C) 2010-2011 yvolk (Yuri Volkov), http://yurivolkov.com
 *
 * 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 com.xorcode.andtweet;

import java.net.SocketTimeoutException;
import java.text.MessageFormat;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.RingtonePreference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;

import com.xorcode.andtweet.data.MyPreferences;
import com.xorcode.andtweet.net.ConnectionAuthenticationException;
import com.xorcode.andtweet.net.ConnectionCredentialsOfOtherUserException;
import com.xorcode.andtweet.net.ConnectionException;
import com.xorcode.andtweet.net.ConnectionOAuth;
import com.xorcode.andtweet.net.ConnectionUnavailableException;
import com.xorcode.andtweet.net.OAuthKeys;
import com.xorcode.andtweet.util.MyLog;
import com.xorcode.andtweet.TwitterUser.CredentialsVerified;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.commonshttp.CommonsHttpOAuthConsumer;
import oauth.signpost.commonshttp.CommonsHttpOAuthProvider;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.exception.OAuthNotAuthorizedException;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * Application settings
 * 
 * @author torgny.bjers
 */
public class PreferencesActivity extends PreferenceActivity
        implements OnSharedPreferenceChangeListener, OnPreferenceChangeListener {

    private static final String TAG = PreferencesActivity.class.getSimpleName();

    public static final String INTENT_RESULT_KEY_AUTHENTICATION = "authentication";

    /** 
     * The URI is consistent with "scheme" and "host" in AndroidManifest
     */
    public static final Uri CALLBACK_URI = Uri.parse("andtweet-oauth://twitt");

    /**
     * This is single list of (in fact, enums...) of Message/Dialog IDs
     */
    public static final int MSG_NONE = 7;

    public static final int MSG_ACCOUNT_VALID = 1;

    public static final int MSG_ACCOUNT_INVALID = 2;

    public static final int MSG_SERVICE_UNAVAILABLE_ERROR = 3;

    public static final int MSG_CONNECTION_EXCEPTION = 4;

    public static final int MSG_SOCKET_TIMEOUT_EXCEPTION = 5;

    public static final int MSG_CREDENTIALS_OF_OTHER_USER = 6;

    // End Of the list ----------------------------------------

    // private CheckBoxPreference mUseExternalStorage;

    private CheckBoxPreference mOAuth;

    private EditTextPreference mEditTextUsername;

    private EditTextPreference mEditTextPassword;

    private Preference mVerifyCredentials;

    private RingtonePreference mNotificationRingtone;

    private boolean onSharedPreferenceChanged_busy = false;

    /**
     * Use this flag to return from this activity to the TweetListAcivity
     */
    private boolean overrideBackButton = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        addPreferencesFromResource(R.xml.preferences);
        // Default values for the preferences will be set only once
        // and in one place: here
        MyPreferences.setDefaultValues(R.xml.preferences, false);
        if (!MyPreferences.getSharedPreferences(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES, MODE_PRIVATE)
                .getBoolean(PreferenceManager.KEY_HAS_SET_DEFAULT_VALUES, false)) {
            Log.e(TAG, "Default values were not set?!");
        }

        mNotificationRingtone = (RingtonePreference) findPreference(MyPreferences.KEY_RINGTONE_PREFERENCE);
        mOAuth = (CheckBoxPreference) findPreference(MyPreferences.KEY_OAUTH);
        mEditTextUsername = (EditTextPreference) findPreference(MyPreferences.KEY_TWITTER_USERNAME_NEW);
        mEditTextPassword = (EditTextPreference) findPreference(MyPreferences.KEY_TWITTER_PASSWORD);
        mVerifyCredentials = (Preference) findPreference(MyPreferences.KEY_VERIFY_CREDENTIALS);

        mNotificationRingtone.setOnPreferenceChangeListener(this);

        /*
         * mUseExternalStorage = (CheckBoxPreference)
         * getPreferenceScreen().findPreference(KEY_EXTERNAL_STORAGE); if
         * (!Environment
         * .getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
         * mUseExternalStorage.setEnabled(false);
         * mUseExternalStorage.setChecked(false); }
         */
    }

    /**
     * Some "preferences" may be changed in TwitterUser object
     */
    private void showUserPreferences(TwitterUser tuIn) {
        TwitterUser tu = tuIn;
        if (tu == null) {
            tu = TwitterUser.getTwitterUser();
        }
        if (mEditTextUsername.getText() == null || tu.getUsername().compareTo(mEditTextUsername.getText()) != 0) {
            mEditTextUsername.setText(tu.getUsername());
        }
        StringBuilder sb = new StringBuilder(this.getText(R.string.summary_preference_username));
        if (tu.getUsername().length() > 0) {
            sb.append(": " + tu.getUsername());
        } else {
            sb.append(": (" + this.getText(R.string.not_set) + ")");
        }
        mEditTextUsername.setSummary(sb);

        // Disable Username if we have no user accounts yet
        mEditTextUsername.setEnabled(TwitterUser.countOfAuthenticatedUsers() > 0);

        if (tu.isOAuth() != mOAuth.isChecked()) {
            mOAuth.setChecked(tu.isOAuth());
        }
        // In fact, we should hide it if not enabled, but I couldn't find an easy way for this...
        mOAuth.setEnabled(tu.canChangeOAuth());

        if (mEditTextPassword.getText() == null || tu.getPassword().compareTo(mEditTextPassword.getText()) != 0) {
            mEditTextPassword.setText(tu.getPassword());
        }
        sb = new StringBuilder(this.getText(R.string.summary_preference_password));
        if (tu.getPassword().length() == 0) {
            sb.append(": (" + this.getText(R.string.not_set) + ")");
        }
        mEditTextPassword.setSummary(sb);
        mEditTextPassword.setEnabled(tu.getConnection().isPasswordNeeded());

        int titleResId;
        switch (tu.getCredentialsVerified()) {
        case SUCCEEDED:
            titleResId = R.string.title_preference_verify_credentials;
            sb = new StringBuilder(this.getText((com.xorcode.andtweet.util.Build.VERSION.SDK_INT >= 8)
                    ? R.string.summary_preference_verify_credentials
                    : R.string.summary_preference_verify_credentials_2lines));
            break;
        default:
            if (tu.isTemporal()) {
                titleResId = R.string.title_preference_verify_credentials_add;
                sb = new StringBuilder(this.getText(R.string.summary_preference_verify_credentials_add));
            } else {
                titleResId = R.string.title_preference_verify_credentials_failed;
                sb = new StringBuilder(this.getText(R.string.summary_preference_verify_credentials_failed));
            }
            break;
        }
        mVerifyCredentials.setTitle(titleResId);

        mVerifyCredentials.setSummary(sb);
        mVerifyCredentials.setEnabled(tu.getCredentialsPresent() || tu.isOAuth());
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Stop service to force preferences reload on the next start
        // Plus disable repeating alarms for awhile (till next start service...)
        AndTweetServiceManager.stopAndTweetService(this, true);

        showAllPreferences();
        MyPreferences.getDefaultSharedPreferences().registerOnSharedPreferenceChangeListener(this);

        Uri uri = getIntent().getData();
        if (uri != null) {
            if (MyLog.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "uri=" + uri.toString());
            }
            if (CALLBACK_URI.getScheme().equals(uri.getScheme())) {
                // To prevent repeating of this task
                getIntent().setData(null);
                // This activity was started by Twitter ("Service Provider")
                // so start second step of OAuth Authentication process
                new OAuthAcquireAccessTokenTask().execute(uri);
                // and return back to default screen
                overrideBackButton = true;
            }
        }
    }

    /**
     * Verify credentials
     * 
     * @param true - Verify only if we didn't do this yet
     */
    private void verifyCredentials(boolean reVerify) {
        TwitterUser tu = TwitterUser.getTwitterUser();
        if (reVerify || tu.getCredentialsVerified() == CredentialsVerified.NEVER) {
            if (tu.getCredentialsPresent()) {
                // Credentials are present, so we may verify them
                // This is needed even for OAuth - to know Twitter Username
                new VerifyCredentialsTask().execute();
            } else {
                if (tu.isOAuth() && reVerify) {
                    // Credentials are not present,
                    // so start asynchronous OAuth Authentication process 
                    new OAuthAcquireRequestTokenTask().execute();
                }
            }

        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        MyPreferences.getDefaultSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }

    /**
     * Show values of all preferences in the "summaries".
     * @see <a href="http://stackoverflow.com/questions/531427/how-do-i-display-the-current-value-of-an-android-preference-in-the-preference-sum"> 
       How do I display the current value of an Android Preference 
       in the Preference summary?</a>
     */
    protected void showAllPreferences() {
        showUserPreferences(null);
        showFrequency();
        showHistorySize();
        showHistoryTime();
        showRingtone(
                MyPreferences.getDefaultSharedPreferences().getString(MyPreferences.KEY_RINGTONE_PREFERENCE, null));
        showMinLogLevel();
    }

    protected void showHistorySize() {
        showListPreference(MyPreferences.KEY_HISTORY_SIZE, R.array.history_size_keys, R.array.history_size_display,
                R.string.summary_preference_history_size);
    }

    protected void showHistoryTime() {
        showListPreference(MyPreferences.KEY_HISTORY_TIME, R.array.history_time_keys, R.array.history_time_display,
                R.string.summary_preference_history_time);
    }

    protected void showFrequency() {
        showListPreference(MyPreferences.KEY_FETCH_FREQUENCY, R.array.fetch_frequency_keys,
                R.array.fetch_frequency_display, R.string.summary_preference_frequency);
    }

    protected void showMinLogLevel() {
        showListPreference(MyPreferences.KEY_MIN_LOG_LEVEL, R.array.log_level_keys, R.array.log_level_display,
                R.string.summary_preference_min_log_level);
    }

    protected void showListPreference(String keyPreference, int keysR, int displayR, int summaryR) {
        String displayParm = "";
        ListPreference lP = (ListPreference) findPreference(keyPreference);
        if (lP != null) {
            String[] k = getResources().getStringArray(keysR);
            String[] d = getResources().getStringArray(displayR);
            displayParm = d[0];
            String listValue = lP.getValue();
            for (int i = 0; i < k.length; i++) {
                if (listValue.equals(k[i])) {
                    displayParm = d[i];
                    break;
                }
            }
        } else {
            displayParm = keyPreference + " was not found";
        }
        MessageFormat sf = new MessageFormat(getText(summaryR).toString());
        lP.setSummary(sf.format(new Object[] { displayParm }));
    }

    protected void showRingtone(Object newValue) {
        String ringtone = (String) newValue;
        Uri uri;
        Ringtone rt;
        if (ringtone == null) {
            uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        } else if ("".equals(ringtone)) {
            mNotificationRingtone.setSummary(R.string.summary_preference_no_ringtone);
        } else {
            uri = Uri.parse(ringtone);
            rt = RingtoneManager.getRingtone(this, uri);
            mNotificationRingtone.setSummary(rt.getTitle(this));
        }
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (PreferencesActivity.this.mCredentialsAreBeingVerified) {
            return;
        }
        if (onSharedPreferenceChanged_busy) {
            return;
        }
        onSharedPreferenceChanged_busy = true;

        try {
            String value = "(not set)";
            if (sharedPreferences.contains(key)) {
                try {
                    value = sharedPreferences.getString(key, "");
                } catch (ClassCastException e) {
                    value = "(not string)";
                }
            }
            MyLog.d(TAG, "onSharedPreferenceChanged: " + key + "='" + value + "'");

            // Remember when last changes were made
            sharedPreferences.edit()
                    .putLong(MyPreferences.KEY_PREFERENCES_CHANGE_TIME, java.lang.System.currentTimeMillis())
                    .commit();

            TwitterUser tu = TwitterUser.getTwitterUser();
            String usernameNew = sharedPreferences.getString(MyPreferences.KEY_TWITTER_USERNAME_NEW, "");

            if (key.equals(MyPreferences.KEY_OAUTH)) {
                // Here and below:
                // Check if there are changes to avoid "ripples"
                if (tu.isOAuth() != mOAuth.isChecked()) {
                    tu = TwitterUser.getAddEditTwitterUser(usernameNew);
                    tu.setCurrentUser();
                    showUserPreferences(tu);
                }
            }
            if (key.equals(MyPreferences.KEY_TWITTER_USERNAME_NEW)) {
                String usernameOld = sharedPreferences.getString(MyPreferences.KEY_TWITTER_USERNAME, "");
                if (usernameNew.compareTo(usernameOld) != 0) {
                    // Try to find existing TwitterUser by the new Username 
                    // without clearing Auth information
                    tu = TwitterUser.getTwitterUser(usernameNew);
                    tu.setCurrentUser();
                    showUserPreferences(tu);
                }
            }
            if (key.equals(MyPreferences.KEY_TWITTER_PASSWORD)) {
                if (tu.getPassword().compareTo(mEditTextPassword.getText()) != 0) {
                    tu = TwitterUser.getAddEditTwitterUser(usernameNew);
                    tu.setCurrentUser();
                    showUserPreferences(tu);
                }
            }
            if (key.equals(MyPreferences.KEY_FETCH_FREQUENCY)) {
                showFrequency();
            }
            if (key.equals(MyPreferences.KEY_RINGTONE_PREFERENCE)) {
                // TODO: Try to move it here from onPreferenceChange...
                // updateRingtone();
            }
            if (key.equals(MyPreferences.KEY_HISTORY_SIZE)) {
                showHistorySize();
            }
            if (key.equals(MyPreferences.KEY_HISTORY_TIME)) {
                showHistoryTime();
            }
            if (key.equals(MyPreferences.KEY_MIN_LOG_LEVEL)) {
                showMinLogLevel();
            }
        } finally {
            onSharedPreferenceChanged_busy = false;
        }
    };

    public boolean onPreferenceChange(Preference preference, Object newValue) {
        if (preference.getKey().equals(MyPreferences.KEY_RINGTONE_PREFERENCE)) {
            showRingtone(newValue);
            return true;
        }
        return false;
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        int titleId = 0;
        int summaryId = 0;

        switch (id) {
        case MSG_ACCOUNT_INVALID:
            if (titleId == 0) {
                titleId = R.string.dialog_title_authentication_failed;
                summaryId = R.string.dialog_summary_authentication_failed;
            }
        case MSG_SERVICE_UNAVAILABLE_ERROR:
            if (titleId == 0) {
                titleId = R.string.dialog_title_service_unavailable;
                summaryId = R.string.dialog_summary_service_unavailable;
            }
        case MSG_SOCKET_TIMEOUT_EXCEPTION:
            if (titleId == 0) {
                titleId = R.string.dialog_title_connection_timeout;
                summaryId = R.string.dialog_summary_connection_timeout;
            }
        case MSG_CREDENTIALS_OF_OTHER_USER:
            if (titleId == 0) {
                titleId = R.string.dialog_title_authentication_failed;
                summaryId = R.string.error_credentials_of_other_user;
            }
            return new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(titleId)
                    .setMessage(summaryId)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface Dialog, int whichButton) {
                        }
                    }).create();

        default:
            return super.onCreateDialog(id);
        }
    }

    /**
     * This semaphore helps to avoid ripple effect: changes in TwitterUser cause
     * changes in this activity ...
     */
    private boolean mCredentialsAreBeingVerified = false;

    /*
     * (non-Javadoc)
     * @seeandroid.preference.PreferenceActivity#onPreferenceTreeClick(android.
     * preference.PreferenceScreen, android.preference.Preference)
     */
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        MyLog.d(TAG, "Preference clicked:" + preference.toString());
        if (preference.getKey().compareTo(MyPreferences.KEY_VERIFY_CREDENTIALS) == 0) {
            verifyCredentials(true);
        }
        return super.onPreferenceTreeClick(preferenceScreen, preference);
    };

    /**
     * Assuming we already have credentials to verify, verify them
     * @author yvolk
     *
     */
    private class VerifyCredentialsTask extends AsyncTask<Void, Void, JSONObject> {
        private ProgressDialog dlg;
        private boolean skip = false;

        @Override
        protected void onPreExecute() {
            dlg = ProgressDialog.show(PreferencesActivity.this, getText(R.string.dialog_title_checking_credentials),
                    getText(R.string.dialog_summary_checking_credentials), true, // indeterminate
                    // duration
                    false); // not cancel-able

            if (PreferencesActivity.this.mCredentialsAreBeingVerified) {
                skip = true;
            } else {
                PreferencesActivity.this.mCredentialsAreBeingVerified = true;
            }
        }

        @Override
        protected JSONObject doInBackground(Void... arg0) {
            JSONObject jso = null;

            int what = MSG_NONE;
            String message = "";

            if (!skip) {
                what = MSG_ACCOUNT_INVALID;
                String usernameUI = "";
                try {
                    usernameUI = MyPreferences.getDefaultSharedPreferences()
                            .getString(MyPreferences.KEY_TWITTER_USERNAME_NEW, "");
                    TwitterUser tu = TwitterUser.getTwitterUser();
                    if (tu.verifyCredentials(true)) {
                        what = MSG_ACCOUNT_VALID;
                        tu.setCurrentUser();
                        // Maybe after successful Verification we should change Current User?
                        if (tu.getUsername().compareTo(usernameUI) != 0) {
                            MyLog.v(TAG, "Changing Username for UI from '" + usernameUI + "' to '"
                                    + tu.getUsername() + "'");
                            MyPreferences.getDefaultSharedPreferences().edit()
                                    .putString(MyPreferences.KEY_TWITTER_USERNAME_NEW, tu.getUsername()).commit();
                        }
                    }
                } catch (ConnectionException e) {
                    what = MSG_CONNECTION_EXCEPTION;
                    message = e.toString();
                } catch (ConnectionAuthenticationException e) {
                    what = MSG_ACCOUNT_INVALID;
                } catch (ConnectionCredentialsOfOtherUserException e) {
                    what = MSG_CREDENTIALS_OF_OTHER_USER;
                    // Switch to this user
                    // New User Name was stored in the Message of the Exception
                    TwitterUser tu = TwitterUser.getTwitterUser(e.getMessage());
                    if (tu.getCredentialsVerified() != CredentialsVerified.SUCCEEDED) {
                        tu.setCredentialsVerified(CredentialsVerified.SUCCEEDED);
                    }
                    what = MSG_ACCOUNT_VALID;

                    tu.setCurrentUser();
                    MyLog.v(TAG,
                            "Changing Username for UI from '" + usernameUI + "' to '" + tu.getUsername() + "'");
                    MyPreferences.getDefaultSharedPreferences().edit()
                            .putString(MyPreferences.KEY_TWITTER_USERNAME_NEW, tu.getUsername()).commit();

                } catch (ConnectionUnavailableException e) {
                    what = MSG_SERVICE_UNAVAILABLE_ERROR;
                } catch (SocketTimeoutException e) {
                    what = MSG_SOCKET_TIMEOUT_EXCEPTION;
                }
            }

            try {
                jso = new JSONObject();
                jso.put("what", what);
                jso.put("message", message);
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return jso;
        }

        /**
         * Credentials were verified just now!
         * This is in the UI thread, so we can mess with the UI
         */
        protected void onPostExecute(JSONObject jso) {
            try {
                dlg.dismiss();
            } catch (Exception e1) {
                // Ignore this error  
            }
            boolean succeeded = false;
            if (jso != null) {
                try {
                    int what = jso.getInt("what");
                    String message = jso.getString("message");

                    switch (what) {
                    case MSG_ACCOUNT_VALID:
                        Toast.makeText(PreferencesActivity.this, R.string.authentication_successful,
                                Toast.LENGTH_SHORT).show();
                        succeeded = true;
                        break;
                    case MSG_ACCOUNT_INVALID:
                    case MSG_SERVICE_UNAVAILABLE_ERROR:
                    case MSG_SOCKET_TIMEOUT_EXCEPTION:
                    case MSG_CREDENTIALS_OF_OTHER_USER:
                        showDialog(what);
                        break;
                    case MSG_CONNECTION_EXCEPTION:
                        int mId = 0;
                        try {
                            mId = Integer.parseInt(message);
                        } catch (Exception e) {
                        }
                        switch (mId) {
                        case 404:
                            mId = R.string.error_twitter_404;
                            break;
                        default:
                            mId = R.string.error_connection_error;
                            break;
                        }
                        Toast.makeText(PreferencesActivity.this, mId, Toast.LENGTH_LONG).show();
                        break;

                    }
                    showUserPreferences(null);
                } catch (JSONException e) {
                    // Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if (!skip) {
                if (succeeded) {
                    TwitterUser.getTwitterUser().setCredentialsVerified(CredentialsVerified.SUCCEEDED);
                } else {
                    TwitterUser.getTwitterUser().setCredentialsVerified(CredentialsVerified.FAILED);
                }
                PreferencesActivity.this.mCredentialsAreBeingVerified = false;
            }
        }
    }

    /**
     * Task 1 of 2 required for OAuth Authentication.
     * See http://www.snipe.net/2009/07/writing-your-first-twitter-application-with-oauth/
     * for good OAuth Authentication flow explanation.
     *  
     * During this task:
     * 1. AndTweet ("Consumer") Requests "Request Token" from Twitter ("Service provider"), 
     * 2. Waits that Request Token
     * 3. Consumer directs User to Service Provider: opens Twitter site in Internet Browser window
     *    in order to Obtain User Authorization.
     * 4. This task ends.
     * 
     * What will occur later:
     * 5. After User Authorized AndTweet in the Internet Browser,
     *    Twitter site will redirect User back to
     *    AndTweet and then the second OAuth task, , will start.
     *   
     * @author yvolk. This code is based on "BLOA" example,
     *         http://github.com/brione/Brion-Learns-OAuth yvolk: I had to move
     *         this code from OAuthActivity here in order to be able to show
     *         ProgressDialog and to get rid of any "Black blank screens"
     */
    private class OAuthAcquireRequestTokenTask extends AsyncTask<Void, Void, JSONObject> {
        private OAuthConsumer mConsumer = null;

        private OAuthProvider mProvider = null;

        private ProgressDialog dlg;

        @Override
        protected void onPreExecute() {
            dlg = ProgressDialog.show(PreferencesActivity.this,
                    getText(R.string.dialog_title_acquiring_a_request_token),
                    getText(R.string.dialog_summary_acquiring_a_request_token), true, // indeterminate
                    // duration
                    false); // not cancel-able
        }

        @Override
        protected JSONObject doInBackground(Void... arg0) {
            JSONObject jso = null;

            // We don't need to worry about any saved states: we can reconstruct
            // the
            // state
            mConsumer = new CommonsHttpOAuthConsumer(OAuthKeys.TWITTER_CONSUMER_KEY,
                    OAuthKeys.TWITTER_CONSUMER_SECRET);

            mProvider = new CommonsHttpOAuthProvider(ConnectionOAuth.TWITTER_REQUEST_TOKEN_URL,
                    ConnectionOAuth.TWITTER_ACCESS_TOKEN_URL, ConnectionOAuth.TWITTER_AUTHORIZE_URL);

            // It turns out this was the missing thing to making standard
            // Activity
            // launch mode work
            mProvider.setOAuth10a(true);

            boolean requestSucceeded = false;
            String message = "";
            String message2 = "";
            try {
                TwitterUser tu = TwitterUser.getTwitterUser();

                // This is really important. If you were able to register your
                // real callback Uri with Twitter, and not some fake Uri
                // like I registered when I wrote this example, you need to send
                // null as the callback Uri in this function call. Then
                // Twitter will correctly process your callback redirection
                String authUrl = mProvider.retrieveRequestToken(mConsumer, CALLBACK_URI.toString());
                saveRequestInformation(tu.getSharedPreferences(), mConsumer.getToken(), mConsumer.getTokenSecret());

                // Start Internet Browser
                PreferencesActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));

                requestSucceeded = true;
            } catch (OAuthMessageSignerException e) {
                message = e.getMessage();
                e.printStackTrace();
            } catch (OAuthNotAuthorizedException e) {
                message = e.getMessage();
                e.printStackTrace();
            } catch (OAuthExpectationFailedException e) {
                message = e.getMessage();
                e.printStackTrace();
            } catch (OAuthCommunicationException e) {
                message = e.getMessage();
                e.printStackTrace();
            }

            try {
                // mSp.edit().putBoolean(ConnectionOAuth.REQUEST_SUCCEEDED,
                // requestSucceeded).commit();
                if (!requestSucceeded) {
                    message2 = PreferencesActivity.this.getString(R.string.dialog_title_authentication_failed);
                    if (message != null && message.length() > 0) {
                        message2 = message2 + ": " + message;
                    }
                    MyLog.d(TAG, message2);
                }

                // This also works sometimes, but message2 may have quotes...
                // String jss = "{\n\"succeeded\": \"" + requestSucceeded
                // + "\",\n\"message\": \"" + message2 + "\"}";
                // jso = new JSONObject(jss);

                jso = new JSONObject();
                jso.put("succeeded", requestSucceeded);
                jso.put("message", message2);
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return jso;
        }

        // This is in the UI thread, so we can mess with the UI
        protected void onPostExecute(JSONObject jso) {
            try {
                dlg.dismiss();
            } catch (Exception e1) {
                // Ignore this error  
            }
            if (jso != null) {
                try {
                    boolean succeeded = jso.getBoolean("succeeded");
                    String message = jso.getString("message");

                    if (succeeded) {
                        // This may be necessary in order to start properly 
                        // after redirection from Twitter
                        // Because of initializations in onCreate...
                        PreferencesActivity.this.finish();
                    } else {
                        Toast.makeText(PreferencesActivity.this, message, Toast.LENGTH_LONG).show();

                        TwitterUser tu = TwitterUser.getTwitterUser();
                        tu.clearAuthInformation();
                        tu.setCredentialsVerified(CredentialsVerified.FAILED);
                        tu.setCurrentUser();
                        showUserPreferences(tu);
                    }
                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Task 2 of 2 required for OAuth Authentication.
     *  
     * During this task:
     * 1. AndTweet ("Consumer") exchanges "Request Token", 
     *    obtained earlier from Twitter ("Service provider"),
     *    for "Access Token". 
     * 2. Stores the Access token for all future interactions with Twitter.
     * 
     * @author yvolk. This code is based on "BLOA" example,
     *         http://github.com/brione/Brion-Learns-OAuth yvolk: I had to move
     *         this code from OAuthActivity here in order to be able to show
     *         ProgressDialog and to get rid of any "Black blank screens"
     */
    private class OAuthAcquireAccessTokenTask extends AsyncTask<Uri, Void, JSONObject> {
        private OAuthConsumer mConsumer = null;

        private OAuthProvider mProvider = null;

        private ProgressDialog dlg;

        @Override
        protected void onPreExecute() {
            dlg = ProgressDialog.show(PreferencesActivity.this,
                    getText(R.string.dialog_title_acquiring_an_access_token),
                    getText(R.string.dialog_summary_acquiring_an_access_token), true, // indeterminate
                    // duration
                    false); // not cancel-able
        }

        @Override
        protected JSONObject doInBackground(Uri... uris) {
            JSONObject jso = null;

            // We don't need to worry about any saved states: we can reconstruct
            // the state
            mConsumer = new CommonsHttpOAuthConsumer(OAuthKeys.TWITTER_CONSUMER_KEY,
                    OAuthKeys.TWITTER_CONSUMER_SECRET);

            mProvider = new CommonsHttpOAuthProvider(ConnectionOAuth.TWITTER_REQUEST_TOKEN_URL,
                    ConnectionOAuth.TWITTER_ACCESS_TOKEN_URL, ConnectionOAuth.TWITTER_AUTHORIZE_URL);

            // It turns out this was the missing thing to making standard
            // Activity launch mode work
            mProvider.setOAuth10a(true);

            String message = "";

            boolean authenticated = false;
            TwitterUser tu = TwitterUser.getTwitterUser();

            Uri uri = uris[0];
            if (uri != null && CALLBACK_URI.getScheme().equals(uri.getScheme())) {
                String token = tu.getSharedPreferences().getString(ConnectionOAuth.REQUEST_TOKEN, null);
                String secret = tu.getSharedPreferences().getString(ConnectionOAuth.REQUEST_SECRET, null);

                tu.clearAuthInformation();
                if (!tu.isOAuth()) {
                    Log.e(TAG, "Connection is not of OAuth type ???");
                } else {
                    try {
                        // Clear the request stuff, we've used it already
                        saveRequestInformation(tu.getSharedPreferences(), null, null);

                        if (!(token == null || secret == null)) {
                            mConsumer.setTokenWithSecret(token, secret);
                        }
                        String otoken = uri.getQueryParameter(OAuth.OAUTH_TOKEN);
                        String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);

                        /*
                         * yvolk 2010-07-08: It appeared that this may be not true:
                         * Assert.assertEquals(otoken, mConsumer.getToken()); (e.g.
                         * if User denied access during OAuth...) hence this is not
                         * Assert :-)
                         */
                        if (otoken != null || mConsumer.getToken() != null) {
                            // We send out and save the request token, but the
                            // secret is not the same as the verifier
                            // Apparently, the verifier is decoded to get the
                            // secret, which is then compared - crafty
                            // This is a sanity check which should never fail -
                            // hence the assertion
                            // Assert.assertEquals(otoken,
                            // mConsumer.getToken());

                            // This is the moment of truth - we could throw here
                            mProvider.retrieveAccessToken(mConsumer, verifier);
                            // Now we can retrieve the goodies
                            token = mConsumer.getToken();
                            secret = mConsumer.getTokenSecret();
                            authenticated = true;
                        }
                    } catch (OAuthMessageSignerException e) {
                        message = e.getMessage();
                        e.printStackTrace();
                    } catch (OAuthNotAuthorizedException e) {
                        message = e.getMessage();
                        e.printStackTrace();
                    } catch (OAuthExpectationFailedException e) {
                        message = e.getMessage();
                        e.printStackTrace();
                    } catch (OAuthCommunicationException e) {
                        message = e.getMessage();
                        e.printStackTrace();
                    } finally {
                        if (authenticated) {
                            tu.saveAuthInformation(token, secret);
                        }
                    }
                }
            }

            try {
                jso = new JSONObject();
                jso.put("succeeded", authenticated);
                jso.put("message", message);
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return jso;
        }

        // This is in the UI thread, so we can mess with the UI
        protected void onPostExecute(JSONObject jso) {
            try {
                dlg.dismiss();
            } catch (Exception e1) {
                // Ignore this error  
            }
            if (jso != null) {
                try {
                    boolean succeeded = jso.getBoolean("succeeded");
                    String message = jso.getString("message");

                    MyLog.d(TAG, this.getClass().getName() + " ended, "
                            + (succeeded ? "authenticated" : "authentication failed"));

                    if (succeeded) {
                        // Credentials are present, so we may verify them
                        // This is needed even for OAuth - to know Twitter Username
                        new VerifyCredentialsTask().execute();

                    } else {
                        String message2 = PreferencesActivity.this
                                .getString(R.string.dialog_title_authentication_failed);
                        if (message != null && message.length() > 0) {
                            message2 = message2 + ": " + message;
                            Log.d(TAG, message);
                        }
                        Toast.makeText(PreferencesActivity.this, message2, Toast.LENGTH_LONG).show();

                        TwitterUser tu = TwitterUser.getTwitterUser();
                        tu.clearAuthInformation();
                        tu.setCredentialsVerified(CredentialsVerified.FAILED);
                        tu.setCurrentUser();
                        showUserPreferences(tu);
                    }

                    // Now we can return to the PreferencesActivity
                    // We need new Intent in order to forget that URI from OAuth Service Provider
                    //Intent intent = new Intent(PreferencesActivity.this, PreferencesActivity.class);
                    //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                    //startActivity(intent);

                } catch (JSONException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    public static void saveRequestInformation(SharedPreferences settings, String token, String secret) {
        // null means to clear the old values
        SharedPreferences.Editor editor = settings.edit();
        if (token == null) {
            editor.remove(ConnectionOAuth.REQUEST_TOKEN);
            MyLog.d(TAG, "Clearing Request Token");
        } else {
            editor.putString(ConnectionOAuth.REQUEST_TOKEN, token);
            MyLog.d(TAG, "Saving Request Token: " + token);
        }
        if (secret == null) {
            editor.remove(ConnectionOAuth.REQUEST_SECRET);
            MyLog.d(TAG, "Clearing Request Secret");
        } else {
            editor.putString(ConnectionOAuth.REQUEST_SECRET, secret);
            MyLog.d(TAG, "Saving Request Secret: " + secret);
        }
        editor.commit();

    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0 && overrideBackButton) {
            finish();
            this.sendBroadcast(new Intent(this, TweetListActivity.class));
            return true;
        }
        // TODO Auto-generated method stub
        return super.onKeyDown(keyCode, event);
    }

}