/*
* 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(this, true);
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(), 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");
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);
}
}