I am trying to retrieve status with Twitter4j, but I get a Unfortunately TestProject has stopped error and my app closes. I am also not getting anything in logcat so I do not know where to start troubleshooting.
This is the method executed when a button is pressed, I am basically making an instance of the class which contains all the oAuth setup and the call to the twitter API for the status. I know this should probably be done with an Async call since it deals with network, but for now I just wanted to retrieve a status even if the UI is blocked:
public void onGetStatus(View v){
if(v.getId()==R.id.button1){
GetUserStatus status = new GetUserStatus();
ResponseList<Status> a = status.twitterSettings();
for(Status s: a){
editText.setText(s.getText());
}
}
}
This is the class with all the twitter configurations for my account.
public class GetUserStatus {
public ResponseList<Status> twitterSettings(){
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true);
cb.setOAuthConsumerKey("xxxxxx");
cb.setOAuthConsumerSecret("xxxx");
cb.setOAuthAccessToken("xxxxx");
cb.setOAuthAccessTokenSecret("xxxx");
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();
try {
ResponseList<Status> a = twitter.getUserTimeline(new Paging(1,5));
return a;
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Research I have done
I have been mostly going off twitter4j's website code examples and it seems everything is up to date so I dont think twitter4j is deprecated. I have also looked through other twitter4j questions on StackOverflow.com but most deal with 404 error or some other type of error, while I am not getting any errors.
The issue was StrictMode.ThreadPolicy was introduced since API Level 9 and the default thread policy had been changed since API Level 11, which in short, does not allow network operation
(eg: HttpClient and HttpUrlConnection) get executed on UI thread. If you do this, you get NetworkOnMainThreadException.
Using AsyncTask fixed the issue. I added a nested class within my activity
class GetUserStatus extends AsyncTask < Void, Void, ResponseList < twitter4j.Status >> {
#
Override
protected ResponseList < twitter4j.Status > doInBackground(Void...params) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true);
cb.setOAuthConsumerKey("XXXX");
cb.setOAuthConsumerSecret("XXXXX");
cb.setOAuthAccessToken("XXXXX");
cb.setOAuthAccessTokenSecret("XXXXXXX");
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();
try {
ResponseList < twitter4j.Status > a = twitter.getUserTimeline(new Paging(1, 5));
return a;
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
protected void onPostExecute(ResponseList < twitter4j.Status > stats) {
for (twitter4j.Status a: stats) {
editText.setText(a.getText());
}
}
}
The other issue I was having with logcat not displaying exceptions, was because Eclipse automatically selected a filter, when I chose the option "all messages (no filter)" I was able to see the stack trace.
Related
I have been at this since the weekend and I am at an impasse. I am pretty new to programming and suspect I am in over my head because I have read every link under "Similar Questions" and it either does not apply or confuses me more.
I am using the Twitter4j API and I worked from code sample no. 7 on the twitter4j website on OAuth support at http://twitter4j.org/en/code-examples.html.
As a skill-building project, I want to make an Android celebrity fan app that will download the timeline from the celebrity's public account. The goal is to execute a timeline download of all the tweets. I do not want the user to login to Twitter with this app or post tweets. The app just downloads a timeline in the background and displays the tweets, probably in a list view.
My code is not executing the following line. It seems to just hang there waiting for something to happen.
RequestToken requestToken = twitter.getOAuthRequestToken();
I have internet permissions in manifest. At this point, I am so confused, I do not even know if I have registered my app correctly. I have the four keys (consumer, consumer secret, access, and access secret).
Settings
-Website: made something up
-Application Type: Read Only
-Callback URL: left it blank
-I did not opt in to "Sign In With Twitter."
OAuth Tool
-Request Type: GET
-Request URI: https://api.twitter.com/1/ (probably wrong)
This is my code:
public class TwitterActivity extends Activity
{
Button mButtonTweets;
String JSONString = null;
TextView JSONContent;
class GetTwitterTimeline extends AsyncTask<Void, String, String>
{
#Override
protected String doInBackground(Void... params)
{
try
{
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey("")
.setOAuthConsumerSecret("")
.setOAuthAccessToken("")
.setOAuthAccessTokenSecret("");
TwitterFactory tf = new TwitterFactory(cb.build());
Twitter twitter = tf.getInstance();
try
{
RequestToken requestToken = twitter.getOAuthRequestToken();
AccessToken accessToken = null;
while (accessToken == null)
{
onProgressUpdate(requestToken.getAuthenticationURL());
try
{
accessToken = twitter.getOAuthAccessToken();
}//try
catch(TwitterException te)
{
if (te.getStatusCode() == 401)
{
onProgressUpdate("Unable to get the access token");
}//if
else
{
te.printStackTrace();
}//else
}//catch
}//while
onProgressUpdate("Got Access Token");
onProgressUpdate("Access Token: " + accessToken.getToken());
onProgressUpdate("Access Token Secret: " + accessToken.getTokenSecret());
}//try
catch (IllegalStateException ie)
{
if(!twitter.getAuthorization().isEnabled())
{
onProgressUpdate("OAuth consumer key/secret is not set.");
}//if
}//catch
}//try
catch (TwitterException te)
{
te.printStackTrace();
onProgressUpdate("Failed to get timeline");
}//catch
String JSONString = "JSON content will go here";
return JSONString;
}//doInBackground
protected void onProgressUpdate(String logEntry)
{
Log.d("twitter4j", logEntry);
}
#Override
protected void onPostExecute(String jsonString)
{
JSONString = jsonString;
}
}//end inner class
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_twitter);
new GetTwitterTimeline().execute();
JSONContent = (TextView) findViewById(R.id.textview_tweets);
mButtonTweets = (Button) findViewById(R.id.button_tweets);
mButtonTweets.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
JSONContent.setText(JSONString);
}
});
}
}
Twitter API has been updated. So Request URI: https://api.twitter.com/1/ won't work.
Also AFAIK the way you are trying to make the app won't work out. You need some kind of authentication. I also dumped one of my app after this API change. :(
Read the following link:
https://dev.twitter.com/docs/api/1.1/overview
We have contacted Google about this and we are on chat
The issue seems to be fixed for devices except Samsung phones.
I'm adding a Google+ sign in option to an app per the official instructions. Once the user has selected their account I would like my server to retrieve their Google+ profile info and update their profile on our site to match.
The first part - having the user select a Google account locally - seems to work just fine. When I try to request a token for the selected account, the Google auth dialog displays with the appropriate parameters; however, when I authorize the app using that dialog and re-request the token, GoogleAuthUtil.getToken(...) again throws a UserRecoverableAuthException (NeedPermission, not GooglePlayServicesAvailabilityException) and I get the same dialog asking me to approve!
This behavior is present on a Samsung S3 running Android 4.1.1 (with 3 Google accounts) and an Acer A100 running 4.0.3. It is NOT present on an HTC Glacier running 2.3.4. Instead, the HTC Glacier gives me a valid auth code. All devices have the latest iteration of Google Play Services installed and are using different Google+ accounts.
Anyone seen this before? Where can I start with debugging?
Here's the complete code - is anything obviously awry?
public class MyGooglePlusClient {
private static final String LOG_TAG = "GPlus";
private static final String SCOPES_LOGIN = Scopes.PLUS_LOGIN + " " + Scopes.PLUS_PROFILE;
private static final String ACTIVITIES_LOGIN = "http://schemas.google.com/AddActivity";
private static MyGooglePlusClient myGPlus = null;
private BaseActivity mRequestingActivity = null;
private String mSelectedAccount = null;
/**
* Get the GPlus singleton
* #return GPlus
*/
public synchronized static MyGooglePlusClient getInstance() {
if (myGPlus == null)
myGPlus = new MyGooglePlusClient();
return myGPlus;
}
public boolean login(BaseActivity requester) {
Log.w(LOG_TAG, "Starting login...");
if (mRequestingActivity != null) {
Log.w(LOG_TAG, "Login attempt already in progress.");
return false; // Cannot launch a new request; already in progress
}
mRequestingActivity = requester;
if (mSelectedAccount == null) {
Intent intent = AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, false,
null, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE, null, null);
mRequestingActivity.startActivityForResult(intent, BaseActivity.REQUEST_GPLUS_SELECT);
}
return true;
}
public void loginCallback(String accountName) {
mSelectedAccount = accountName;
authorizeCallback();
}
public void logout() {
Log.w(LOG_TAG, "Logging out...");
mSelectedAccount = null;
}
public void authorizeCallback() {
Log.w(LOG_TAG, "User authorized");
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String token = null;
try {
Bundle b = new Bundle();
b.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, ACTIVITIES_LOGIN);
token = GoogleAuthUtil.getToken(mRequestingActivity,
mSelectedAccount,
"oauth2:server:client_id:"+Constants.GOOGLE_PLUS_SERVER_OAUTH_CLIENT
+":api_scope:" + SCOPES_LOGIN,
b);
} catch (IOException transientEx) {
// Network or server error, try later
Log.w(LOG_TAG, transientEx.toString());
onCompletedLoginAttempt(false);
} catch (GooglePlayServicesAvailabilityException e) {
Log.w(LOG_TAG, "Google Play services not available.");
Intent recover = e.getIntent();
mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
} catch (UserRecoverableAuthException e) {
// Recover (with e.getIntent())
Log.w(LOG_TAG, "User must approve "+e.toString());
Intent recover = e.getIntent();
mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
} catch (GoogleAuthException authEx) {
// The call is not ever expected to succeed
Log.w(LOG_TAG, authEx.toString());
onCompletedLoginAttempt(false);
}
Log.w(LOG_TAG, "Finished with task; token is "+token);
if (token != null) {
authorizeCallback(token);
}
return token;
}
};
task.execute();
}
public void authorizeCallback(String token) {
Log.w(LOG_TAG, "Token obtained: "+token);
// <snipped - do some more stuff involving connecting to the server and resetting the state locally>
}
public void onCompletedLoginAttempt(boolean success) {
Log.w(LOG_TAG, "Login attempt "+(success ? "succeeded" : "failed"));
mRequestingActivity.hideProgressDialog();
mRequestingActivity = null;
}
}
I've had this issue for a while and came up with a proper solution.
String token = GoogleAuthUtil.getToken(this, accountName, scopeString, appActivities);
This line will either return the one time token or will trigger the UserRecoverableAuthException.
On the Google Plus Sign In guide, it says to open the proper recovery activity.
startActivityForResult(e.getIntent(), RECOVERABLE_REQUEST_CODE);
When the activity returns with the result, it will come back with few extras in the intent and that is where the new token resides :
#Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
if (requestCode == RECOVERABLE_REQUEST_CODE && responseCode == RESULT_OK) {
Bundle extra = intent.getExtras();
String oneTimeToken = extra.getString("authtoken");
}
}
With the new oneTimeToken given from the extra, you can submit to the server to connect properly.
I hope this helps!
Its too late to reply but it may help to people having same concern in future.
They have mentioned in the tutorial that it will always throw UserRecoverableAuthException
when you invoke GoogleAuthUtil.getToken() for the first time. Second time it will succeed.
catch (UserRecoverableAuthException e) {
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data. After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
return;
}
i used below code to get access code from google.
execute this new GetAuthTokenFromGoogle().execute(); once from public void onConnected(Bundle connectionHint) and once from protected void onActivityResult(int requestCode, int responseCode, Intent intent)
private class GetAuthTokenFromGoogle extends AsyncTask<Void, Integer, Void>{
#Override
protected void onPreExecute()
{
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
try {
accessCode = GoogleAuthUtil.getToken(mContext, Plus.AccountApi.getAccountName(mGoogleApiClient), SCOPE);
new ValidateTokenWithPhoneOmega().execute();
Log.d("Token -- ", accessCode);
} catch (IOException transientEx) {
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
return null;
} catch (UserRecoverableAuthException e) {
// Recover
startActivityForResult(e.getIntent(), RC_ACCESS_CODE);
e.printStackTrace();
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
authEx.printStackTrace();
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
}
}
I have got around this issue by using a web based login. I open a url like this
String url = "https://accounts.google.com/o/oauth2/auth?scope=" + Scopes.PLUS_LOGIN + "&client_id=" + webLoginClientId + "&response_type=code&access_type=offline&approval_prompt=force&redirect_uri=" + redirect;
The redirect url then handles the response and returns to my app.
In terms of my findings on using the Google Play Services, I've found:
HTC One is 3.1.59 (736673-30) - not working
Galaxy Note is 3.1.59 (736673-36) - not working
Nexus S is 3.1.59 (736673-34) - works
And I'd like to be involved in the chat that is occurring, however I don't have a high enough reputation to do so.
I've experienced the same issue recently - it appears to be device-specific (I had it happen every time on one S3, but on another S3 running the same OS it didn't happen, even with the same account). My hunch is that it's a bug in a client app, either the G+ app or the Google Play Services app. I managed to solve the issue on one of my devices by factory resetting it (a Motorola Defy), then reinstalling the Google Play Services app, but that's a completely useless solution to tell to users.
Edit (6th Aug 2013): This seems to have been fixed for me without any changes to my code.
The first potential issue I can see is that you are calling GoogleAuthUtil.getToken() after you get the onConnected() callback. This is a problem because requesting an authorization code for your server using GoogleAuthUtil.getToken() will always show a consent screen to your users. So you should only get an authorization code for new users and, to avoid showing new users two consent screens, you must fetch an authorization code and exchange it on your server before resolving any connection failures from PlusClient.
Secondly, make sure you actually need both a PlusClient and an authorization code for your servers. You only need to get a PlusClient and an authorization code if you are intending to make calls to the Google APIs from both the Android client and your server. As explained in this answer.
These issues would only result in two consent dialogs being displayed (which is clearly not an endless loop) - are you seeing more than two consent dialogs?
I had a similar problem where an apparent auth loop kept creating {read: spamming} these "Signing In..." and Permission request dialogs while also giving out the discussed exception repeatedly.
The problem appears in some slightly-modified example code that I (and other like me, I suspect) "cargo-culted" from AndroidHive. The solution that worked for me was ensuring that only one background token-retrieval task runs at the background at any given time.
To make my code easier to follow, here's the auth flow in my app (that is almost identical to the example code on AndoidHive): Activity -> onConnected(...) -> getProfileInformation() -> getOneTimeToken().
Here's where getOneTimeToken() is called:
private void getProfileInformation() {
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
Person currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
getOneTimeToken(); // <-------
...
Here's my getOneTimeToken():
private void getOneTimeToken(){
if (task==null){
task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
LogHelper.log('d',LOGTAG, "Executing background task....");
Bundle appActivities = new Bundle();
appActivities.putString(
GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
ACTIVITIES_LOGIN);
String scopes = "oauth2:server" +
":client_id:" + SERVER_CLIENT_ID +
":api_scope:" + SCOPES_LOGIN;
String token = null;
try {
token = GoogleAuthUtil.getToken(
ActivityPlus.this,
Plus.AccountApi.getAccountName(mGoogleApiClient),
scopes,
appActivities
);
} catch (IOException transientEx) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, transientEx.toString());
} catch (UserRecoverableAuthException e) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, e.toString());
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST);
} catch (GoogleAuthException authEx) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, authEx.toString());
} catch (IllegalStateException stateEx){
LogHelper.log('e',LOGTAG, stateEx.toString());
}
LogHelper.log('d',LOGTAG, "Background task finishing....");
return token;
}
#Override
protected void onPostExecute(String token) {
LogHelper.log('i',LOGTAG, "Access token retrieved: " + token);
}
};
}
LogHelper.log('d',LOGTAG, "Task setup successful.");
if(task.getStatus() != AsyncTask.Status.RUNNING){
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); //double safety!
} else
LogHelper.log('d',LOGTAG,
"Attempted to restart task while it is running!");
}
Please note that I have a {probably redundant} double-safety against the task executing multiple times:
if(task .getStatus() != AsyncTask.Status.RUNNING){...} - ensures that the task isn't running before attempting to execute it.
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);- makes sure that copies of this task are "synchronized" (i.e. a queue is in place such that only one task of this type can executed at a given time).
P.S.
Minor clarification: LogHelper.log('e',...) is equivalent to Log.e(...) etc.
you should startactiviy in UI thread
try {
....
} catch (IOException transientEx) {
....
} catch (final UserRecoverableAuthException e) {
....
runOnUiThread(new Runnable() {
public void run() {
startActivityForResult(e1.getIntent(), AUTH_CODE_REQUEST);
}
});
}
Had the same bug with infinite loop of permission request. For me it was because time on my phone was shifted. When I check detect time automatically this bug disappeared. Hope this helps!
I am having a hard time trying to figure out what's going on. I have the typical app structure that spawns one OAuthActivity that takes care of getting a twitter token, then my main application activity uses that token for various twitter-related operations.
My OAuthActivity works. I get an auth token, and the Twitter web screen correctly shows my application name, etc.... Besides, inside that activity, I can send a tweet and it gets published. . This means the OAuthActivity works, the clock is in sync, the token is valid, etc...
But when this OAuthActivity finishes and returns to the calling activity, whenever I try to use that token (recreating it from the persisted key/secret), no matter for what, the operation always fails with a 401, complaining that AuthChallenge reported null... just like if I provided an empty token, but i haven't !!!
Please find attached the source of my OAuthActivity, and the source of how I initialize Twitter Objects in the main activity. Please tell me if you see something wrong.
PD - I have obviously checked that the token values I assign are the same I get !! Also tried different ways of instantiating Twitter, via properties, via builder, via sets .... and nothing changes :(
EDIT-> I found around I have to call "verifyCredentials()" on the new twitter object if I want to reuse a token, but .... no luck! (please find posted exception at the end)
EDIT-2> If I use on both the child activity and the parent
mTwitter=TwitterFactory.getSingleton()
then the twitter object works, but this is not really acceptable for me because It doesnt use persistance, and I would need to authorize the application everytime. Besides, only Twitter object is authorized, TwitterStream keeps throwing exceptions.
Cheers!
Source code of the parent activity, where I try to use an access token obtained in the child activity, listed below. Whatever I try to do with this token always gets the 401.
private void init_twitter(String tok, String sec) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(Conf.OAUTH_CONSUMER_KEY)
.setOAuthConsumerSecret(Conf.OAUTH_CONSUMER_SECRET)
.setOAuthAccessToken(tok)
.setOAuthAccessTokenSecret(sec);
TwitterFactory tf = new TwitterFactory(cb.build());
mTwitter=tf.getInstance();
/** This always fails, even though I call this routine with the
correct token & secret !!! See at the enf of message for an alternate
routine like this one that makes use of verifyCredentials and
also fails. */
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
mTwitter.updateStatus("yello 2");
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
}
SOurce Code of the child OAuthActivity, it apparently works as I get an access token & am able to tweet:
import a lot;
public class TwitterLogin extends Activity {
private final String TAG = "TwitterLogin";
public final static String PREF_KEY_OAUTH_TOKEN="twitter.oauth.token", PREF_KEY_OAUTH_SECRET="twitter.oauth.secret", PREF_KEY_TWITTER_LOGIN="twitter.oauth.login";
private SharedPreferences mPreferences;
private Twitter twitter = new TwitterFactory().getInstance();
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "Starting task to retrieve request token.");
this.mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
super.onCreate(savedInstanceState);
getActionBar().setTitle("TWITTER AUTHENTICATION");
}
private void returnParent(boolean result) {
setResult(result?Activity.RESULT_OK:Activity.RESULT_CANCELED, null);
if (Conf.LOG_ON) Log.d(TAG, "TWITTER AUTH: END PROCESS , GLOBAL RESULT "+result);
/** THE FOLLOWING THING WORKS !!!!! IT SUCCESSFULLY TWEETS */
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
twitter.updateStatus("yello");
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
finish();
}
/**
* Uses TWITTER4J to get the Request URL. It gets something like
* AUTH URL TWITTER4J IS http://api.twitter.com/oauth/authorize?oauth_token=xxxxxxxxxxxxxxxxxxxxx
*
* #return The Request URL to open in webview and get the Verifier
*/
private String oauth_twitter4j_getRequestUrl() throws TwitterException {
twitter.setOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
RequestToken tempToken = twitter.getOAuthRequestToken(Constants.OAUTH_CALLBACK_URL);
return tempToken.getAuthorizationURL();
}
#Override
protected void onResume() {
super.onResume();
WebView webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(true);
webview.setVisibility(View.VISIBLE);
setContentView(webview);
Log.i(TAG, "Retrieving request token from Google servers");
try {
StrictMode.ThreadPolicy policy = new StrictMode. ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);
String authorizationUrl=oauth_twitter4j_getRequestUrl();
Log.d(TAG, "AUTH URL TWITTER4J IS "+authorizationUrl_t);
webview.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (Conf.LOG_ON) Log.d(TAG,"WebView: "+url);
if (url != null && url.startsWith(Constants.OAUTH_CALLBACK_URL)) try {
System.out.println("TWEET TWEET TWEET");
retrieveAccessToken(url); //added this
webView.setVisibility(View.GONE); //added this
return true;
} catch (Exception e) {
e.printStackTrace();
returnParent(false);
return true;
} else return false;
}
private void saveAccessToken(AccessToken accessToken) {
// Shared Preferences
Editor e = mPreferences.edit();
// After getting access token, access token secret
// store them in application preferences
e.putString(PREF_KEY_OAUTH_TOKEN, accessToken.getToken());
e.putString(PREF_KEY_OAUTH_SECRET,accessToken.getTokenSecret());
e.putBoolean(PREF_KEY_TWITTER_LOGIN, true);
e.commit();
Log.e("Twitter OAuth Token", "> " + accessToken.getToken()+"-"+accessToken.getScreenName());
}
private void retrieveAccessToken(String url) throws Exception {
String requestToken = extractParamFromUrl(url,"oauth_token");
String verifier= extractParamFromUrl(url,"oauth_verifier");
if (Conf.LOG_ON) Log.d(TAG, "Tenemos ACCESS TOKEN y VERIFIER :"+requestToken+","+verifier+","+(new Date().toString()));
if (ONLY_TWITTER4J)
retrieveAccessToken_with4j(verifier);
else
retrieveAccessToken_signpost(verifier);
}
private void retrieveAccessToken_with4j(String verifier) throws TwitterException {
AccessToken a=twitter.getOAuthAccessToken(verifier);
saveAccessToken(a);
returnParent(true);
}
private String extractParamFromUrl(String url,String paramName) {
String queryString = url.substring(url.indexOf("?", 0)+1,url.length());
QueryStringParser queryStringParser = new QueryStringParser(queryString);
return queryStringParser.getQueryParamValue(paramName);
}
});
webview.loadUrl(authorizationUrl);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Exception I get when calling VerifyCredentials with the token I'm sure is right:
Received authentication challenge is null
W/System.err(24915): Relevant discussions can be found on the Internet at:
W/System.err(24915): http://www.google.co.jp/search?q=6f0f59ca or
W/System.err(24915): http://www.google.co.jp/search?q=20d0f74e
W/System.err(24915): TwitterException{exceptionCode=[6f0f59ca-20d0f74e 1de2170b-f94dee38], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=3.0.3}
W/System.err(24915): at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:192)
W/System.err(24915): at twitter4j.internal.http.HttpClientWrapper.request(HttpClientWrapper.java:61)
W/System.err(24915): at twitter4j.internal.http.HttpClientWrapper.get(HttpClientWrapper.java:89)
W/System.err(24915): at twitter4j.TwitterBaseImpl.fillInIDAndScreenName(TwitterBaseImpl.java:126)
W/System.err(24915): at twitter4j.TwitterImpl.verifyCredentials(TwitterImpl.java:592)
W/System.err(24915): at com.regaliz.helpers.TwitterManager$2.run(TwitterManager.java:140)
W/System.err(24915): at java.lang.Thread.run(Thread.java:856)
W/System.err(24915): Caused by: java.io.IOException: Received authentication challenge is null
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.processAuthHeader(HttpURLConnectionImpl.java:397)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.processResponseHeaders(HttpURLConnectionImpl.java:345)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:276)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:479)
W/System.err(24915): at twitter4j.internal.http.HttpResponseImpl.<init>(HttpResponseImpl.java:34)
W/System.err(24915): at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:156)
W/System.err(24915): ... 6 more
This is the function modified to make use of verifyCredentials:
private void init_twitter_2(final String tok, final String sec) throws TwitterException {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(Conf.OAUTH_CONSUMER_KEY)
.setOAuthConsumerSecret(Conf.OAUTH_CONSUMER_SECRET);
// .setOAuthAccessToken(tok)
// .setOAuthAccessTokenSecret(sec);
TwitterFactory tf = new TwitterFactory(cb.build());
mTwitter=tf.getInstance();
Log.d(TAG, "init_twitter_2 "+tok+","+sec);
new Thread(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
User u;
try {
/** also tried setting token&secret like this, instead of in the builder-->no success */
mTwitter.setOAuthAccessToken(new AccessToken(tok,sec));
u = mTwitter.verifyCredentials();
Log.d(TAG, "User: "+u.getName());
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
}
There are stupid people, stupid people, stupid people, and then it's me. For one week I've been struggling with the code, tracing Twitter4j, replicating oauths with curl, suspecting of garbage-collected activities, tracing DDMS, calculating hashes on tokens .... only to find I had 2 instances of Conf.OAUTH_CONSUMER_xxxxx with different values.
As the stuff came from constants, and the names were similar, I didn't realized that.
sigh -- 50 reputation points down the toilet!
While implementing Twitter integration in my app, I discovered the following oddness of the Eclipse debugger. What is causing this?
I'm using this AsyncTask for getting a request token from twitter using twitter4j 3.0.3.
public class TwitterRequestAsync extends AsyncTask<Void, Void, RequestToken> {
private Context context;
public TwitterRequestAsync(Context context) {
this.context = context;
}
#Override
protected RequestToken doInBackground( Void... params ) {
Twitter twitter = getTwitter(); // getTwitter() is in enclosing class
try {
RequestToken token = twitter.getOAuthRequestToken();
return token;
}
catch (TwitterException e) {
Log.e( TAG, e.getMessage(), e );
return null;
}
}
#Override
protected void onPostExecute( RequestToken result ) {
super.onPostExecute( result );
if ( result != null ) {
// stuffs concerning request token here
}
}
}
When I debug this code it appears that there is an exception thrown when getOAuthRequestToken() executes and the next line that the debugger shows executing is in the catch clause, return null;
However, the result that is returned to onPostExecute(...) is a valid request token, so the debugger is doing something weird. I've cleaned my project and restarted Eclipse each multiple times with no change in this behavior. Am I broken?
This is a known issue. It appears that it could be a problem with the Dalvik VM. The last return statement of the method is shown to execute in the debugger.
Changing the doInBackground body to:
#Override
protected RequestToken doInBackground( Void... params ) {
Twitter twitter = getTwitter();
RequestToken token = null;
try {
token = twitter.getOAuthRequestToken();
}
catch (TwitterException e) {
Log.e( TAG, e.getMessage(), e );
}
return token;
}
Causes execution to appear to proceed as expected.
See https://groups.google.com/forum/?fromgroups=#!topic/android-developers/DEU6JmdyFyM for an old mention of the problem with a link to an even older mention. Someone finally created an issue for this at https://code.google.com/p/android/issues/detail?id=34193.
As your self-answer notes, this is a known issue. I wanted to point out that it (and other quirks) are documented in the Dalvik docs. You can find it in the official but raw form, or unofficial but formatted -- skip down to "Known Issues and Limitations".
It would be nice if the documentation was more prominent.
I am using Twitter4J (2.1.0) to try to update tweets. I can't figure out what is wrong with my code.
In particular my problems are:
(a) Not all tweets post successfully. I often get the error code of -1. According to a google group post...
You get code -1 when the internal http component fails to connect to
or read from the API. You may also get code -1 when the API is
unreachable from the JVM due to the DNS related issues.
Strangely I seemed to be getting this pretty much every second post. To deal with this whenever I received the -1 error code I would try to update again. While I realise this is not a very good solution. this fixed the probem 95% of the time
(b) I get a duplication errors (error code 403) whenever the new tweet matches any old tweet
Error code 403 occurs even if the duplicate is now outdated (eg. post "Hello there", post a variety of status updates, then post "Hello there" again throws a TwitterException with error code 403)
My current code...
My code is in an AsyncTask which is in turn in a Service (rather than activity). I have included the Asynctask code and another method below....
class SendTwitAsyncTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
String tokenTwit = params[0];
String tokenSecretTwit = params[1];
String strMessageBody = params[2];
AccessToken aToken = new AccessToken(tokenTwit, tokenSecretTwit);
// initialize Twitter4J
Twitter twitter = new TwitterFactory().getInstance();
twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
twitter.setOAuthAccessToken(aToken);
// create a tweet
// strMessageBody varies
String tweet = strMessageBody;
boolean bool = twitter.isOAuthEnabled();
if (twitter.isOAuthEnabled()) {
GeoLocation geolocation = new GeoLocation(-33.91, 151.25);
try {
twitter.updateStatus(tweet, geolocation);
showNotification("Twitter One" , TWIT_SUCCESS);
} catch (TwitterException te) {
if (te.getStatusCode() == -1) {
//try again
try {
twitter.updateStatus(tweet, geolocation);
showNotification("Twitter Two ", TWIT_SUCCESS);
}
catch (TwitterException tetwo) {
describeTwitException(tetwo.getStatusCode());
}
} //end if
//else exception other than -1
else {
describeTwitException(te.getStatusCode());
} //end else
}// end outer catch
} //end if
else {
showNotification("Unable to authenticate" , TWIT_FAIL);
}//
return null;
}
} //end class SendTwitAsyncTask
public void describeTwitException(int twitExceptionCode) {
switch (twitExceptionCode) {
case (-1):
showNotification("Twitter (unable to connect)", TWIT_FAIL);
break;
case(403):
showNotification("Twitter (duplicate tweet)", TWIT_FAIL);
break;
default:
showNotification("Twitter", TWIT_FAIL);
} //end switch
} //end describeTwitException method
The twitter API will reject any tweet that matches a tweet that you've already made. I don't think the old tweet ever 'expires.'