Project: andtweet
/* 
 * 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.AndTweetPreferences; 
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.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"
 
    public static final String KEY_OAUTH = "oauth"
    /** 
     * The URI is consistent with "scheme" and "host" in AndroidManifest 
     */
 
    public static final Uri CALLBACK_URI = Uri.parse("andtweet-oauth://twitt"); 
 
    /**
     * Was this user ever authenticated? 
     */
 
    public static final String KEY_WAS_AUTHENTICATED = "was_authenticated"
 
    /**
     * Was current user ( user set in global preferences) authenticated last 
     * time credentials were verified? CredentialsVerified.NEVER - after changes 
     * of password/OAuth... 
     */
 
    public static final String KEY_CREDENTIALS_VERIFIED = "credentials_verified"
 
    /**
     * This is sort of button to start verification of credentials 
     */
 
    public static final String KEY_VERIFY_CREDENTIALS = "verify_credentials"
 
    /**
     * Process of authentication was started (by {@link #PreferencesActivity}) 
     */
 
    public static final String KEY_AUTHENTICATING = "authenticating"
 
    /**
     * Current User 
     */
 
    public static final String KEY_TWITTER_USERNAME = "twitter_username"
 
    /**
     * New Username typed / selected in UI 
     * It doesn't immediately change "Current User" 
     */
 
    public static final String KEY_TWITTER_USERNAME_NEW = "twitter_username_new"
     
    public static final String KEY_TWITTER_PASSWORD = "twitter_password"
 
    public static final String KEY_HISTORY_SIZE = "history_size"
 
    public static final String KEY_HISTORY_TIME = "history_time"
 
    public static final String KEY_FETCH_FREQUENCY = "fetch_frequency"
 
    public static final String KEY_AUTOMATIC_UPDATES = "automatic_updates"
 
    public static final String KEY_RINGTONE_PREFERENCE = "notification_ringtone"
 
    // public static final String KEY_EXTERNAL_STORAGE = "storage_use_external"; 
 
    public static final String KEY_CONTACT_DEVELOPER = "contact_developer"
 
    public static final String KEY_REPORT_BUG = "report_bug"
 
    public static final String KEY_CHANGE_LOG = "change_log"
 
    public static final String KEY_ABOUT_APPLICATION = "about_application"
 
    /**
     * System time when shared preferences were changed 
     */
 
    public static final String KEY_PREFERENCES_CHANGE_TIME = "preferences_change_time"
 
    /**
     * System time when shared preferences were examined and took into account 
     * by some receiver. We use this for the Service to track time when it 
     * recreated alarms last time... 
     */
 
    public static final String KEY_PREFERENCES_EXAMINE_TIME = "preferences_examine_time"
     
    /**
     * 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 ListPreference mHistorySizePreference; 
 
    private ListPreference mHistoryTimePreference; 
 
    private ListPreference mFetchFrequencyPreference; 
 
    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 
        AndTweetPreferences.setDefaultValues(R.xml.preferences, false); 
        if (!AndTweetPreferences.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?!");    
        } 
         
        mHistorySizePreference = (ListPreference) findPreference(KEY_HISTORY_SIZE); 
        mHistoryTimePreference = (ListPreference) findPreference(KEY_HISTORY_TIME); 
        mFetchFrequencyPreference = (ListPreference) findPreference(KEY_FETCH_FREQUENCY); 
        mNotificationRingtone = (RingtonePreference) findPreference(KEY_RINGTONE_PREFERENCE); 
        mOAuth = (CheckBoxPreference) findPreference(KEY_OAUTH); 
        mEditTextUsername = (EditTextPreference) findPreference(KEY_TWITTER_USERNAME_NEW); 
        mEditTextPassword = (EditTextPreference) findPreference(KEY_TWITTER_PASSWORD); 
        mVerifyCredentials = (Preference) findPreference(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); 
 
        if (tu.isOAuth() != mOAuth.isChecked()) { 
            mOAuth.setChecked(tu.isOAuth()); 
        } 
 
        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()); 
 
        if (tu.getCredentialsVerified() == CredentialsVerified.SUCCEEDED) { 
            sb = new StringBuilder( 
                    this 
                            .getText((com.xorcode.andtweet.util.Build.VERSION.SDK_INT >= 8) ? R.string.summary_preference_credentials_verified 
                                    : R.string.summary_preference_credentials_verified_2lines)); 
        } else { 
            sb = new StringBuilder(this.getText(R.string.summary_preference_verify_credentials)); 
            if (com.xorcode.andtweet.util.Build.VERSION.SDK_INT >= 8) { 
                // Froyo can show more than two lines 
                sb.append("\n("); 
            } else { 
                sb.append(" ("); 
            } 
            switch (tu.getCredentialsVerified()) { 
                case NEVER: 
                    sb.append(this.getText(R.string.authentication_never)); 
                    break
                case FAILED: 
                    sb.append(this.getText(R.string.dialog_title_authentication_failed)); 
                    break
            } 
            sb.append(")"); 
        } 
 
        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(thistrue); 
         
        showAllPreferences(); 
        AndTweetPreferences.getDefaultSharedPreferences().registerOnSharedPreferenceChangeListener(this); 
         
        Uri uri = getIntent().getData(); 
        if (uri != null) { 
            if (Log.isLoggable(AndTweetService.APPTAG, 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(); 
        AndTweetPreferences.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(AndTweetPreferences.getDefaultSharedPreferences().getString( 
                KEY_RINGTONE_PREFERENCE, null)); 
    } 
     
    protected void showHistorySize() { 
        String[] k = getResources().getStringArray(R.array.history_size_keys); 
        String[] d = getResources().getStringArray(R.array.history_size_display); 
        String displayHistorySize = d[0]; 
        String historySize = mHistorySizePreference.getValue(); 
        for (int i = 0; i < k.length; i++) { 
            if (historySize.equals(k[i])) { 
                displayHistorySize = d[i]; 
                break
            } 
        } 
        MessageFormat sf = new MessageFormat(getText(R.string.summary_preference_history_size) 
                .toString()); 
        mHistorySizePreference.setSummary(sf.format(new Object[] { 
            displayHistorySize 
        })); 
    } 
 
    protected void showHistoryTime() { 
        String[] k = getResources().getStringArray(R.array.history_time_keys); 
        String[] d = getResources().getStringArray(R.array.history_time_display); 
        String displayHistoryTime = d[0]; 
        String historyTime = mHistoryTimePreference.getValue(); 
        for (int i = 0; i < k.length; i++) { 
            if (historyTime.equals(k[i])) { 
                displayHistoryTime = d[i]; 
                break
            } 
        } 
        MessageFormat sf = new MessageFormat(getText(R.string.summary_preference_history_time) 
                .toString()); 
        mHistoryTimePreference.setSummary(sf.format(new Object[] { 
            displayHistoryTime 
        })); 
    } 
 
    protected void showFrequency() { 
        String[] k = getResources().getStringArray(R.array.fetch_frequency_keys); 
        String[] d = getResources().getStringArray(R.array.fetch_frequency_display); 
        String displayFrequency = d[0]; 
        String frequency = mFetchFrequencyPreference.getValue(); 
        for (int i = 0; i < k.length; i++) { 
            if (frequency.equals(k[i])) { 
                displayFrequency = d[i]; 
                break
            } 
        } 
        MessageFormat sf = new MessageFormat(getText(R.string.summary_preference_frequency) 
                .toString()); 
        mFetchFrequencyPreference.setSummary(sf.format(new Object[] { 
            displayFrequency 
        })); 
    } 
 
    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)"
                } 
            } 
            AndTweetService.d(TAG, "onSharedPreferenceChanged: " + key + "='" + value + "'"); 
 
            // Remember when last changes were made 
            sharedPreferences 
                    .edit() 
                    .putLong(PreferencesActivity.KEY_PREFERENCES_CHANGE_TIME, 
                            java.lang.System.currentTimeMillis()).commit(); 
             
            TwitterUser tu = TwitterUser.getTwitterUser(); 
            String usernameNew = sharedPreferences.getString(KEY_TWITTER_USERNAME_NEW, ""); 
             
            if (key.equals(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(KEY_TWITTER_USERNAME_NEW)) { 
                String usernameOld = sharedPreferences.getString(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(KEY_TWITTER_PASSWORD)) { 
                if (tu.getPassword().compareTo(mEditTextPassword.getText()) != 0) { 
                    tu = TwitterUser.getAddEditTwitterUser(usernameNew); 
                    tu.setCurrentUser(); 
                    showUserPreferences(tu); 
                } 
            } 
            if (key.equals(KEY_FETCH_FREQUENCY)) { 
                showFrequency(); 
            } 
            if (key.equals(KEY_RINGTONE_PREFERENCE)) { 
                // TODO: Try to move it here from onPreferenceChange... 
                // updateRingtone(); 
            } 
            if (key.equals(KEY_HISTORY_SIZE)) { 
                showHistorySize(); 
            } 
            if (key.equals(KEY_HISTORY_TIME)) { 
                showHistoryTime(); 
            } 
        } finally { 
            onSharedPreferenceChanged_busy = false
        } 
    }; 
 
    public boolean onPreferenceChange(Preference preference, Object newValue) { 
        if (preference.getKey().equals(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) { 
        AndTweetService.d(TAG, "Preference clicked:" + preference.toString()); 
        if (preference.getKey().compareTo(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; 
                try { 
                    String usernameUI = AndTweetPreferences 
                    .getDefaultSharedPreferences().getString(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) { 
                            AndTweetService.v(TAG, "Changing Username for UI from '" + usernameUI  
                                    + "' to '" + tu.getUsername() + "'"); 
                            AndTweetPreferences 
                            .getDefaultSharedPreferences().edit().putString(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; 
                } 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; 
                    } 
                    Log.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(), nullnull); 
 
                        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"); 
 
                    Log.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); 
            Log.d(TAG, "Clearing Request Token"); 
        } else { 
            editor.putString(ConnectionOAuth.REQUEST_TOKEN, token); 
            Log.d(TAG, "Saving Request Token: " + token); 
        } 
        if (secret == null) { 
            editor.remove(ConnectionOAuth.REQUEST_SECRET); 
            Log.d(TAG, "Clearing Request Secret"); 
        } else { 
            editor.putString(ConnectionOAuth.REQUEST_SECRET, secret); 
            Log.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); 
    } 
     
}