Tuesday 27 December 2011

Android: Twitter login using twitter4j library

At the time of writing the best tutorial I could find for integrating Twitter into an Android app using the twitter4j library was this one...

http://blog.blundell-apps.com/sending-a-tweet/

... I set up Twitter user authentication as in the tutorial above such that the Activity doing the Twitter authentication has its content view changed to a WebView, the Activity is declared in the manifest file containing an intent-filter to catch the Twitter callback and, on callback, the response is processed and the Activity's content view is changed back to whatever it was.

I got it working but in doing so I found (what I think is) an easier way and such that the WebView doesn't ever kick off the phone's web browser app (thereby leaving your app, which you don't really want), as follows:

(1) Remove the Activity singleInstance and intent-filter declarations you added to the manifest file (in following the tutorial above). Don't need that anymore!

(2) Add a WebView to your Activity layout file and set its initial visibility to "gone" and its position, height, width etc however you want it. I set its properties so that it covers the whole screen when visible, as follows:

<WebView
  android:id="@+id/myWebView"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:visibility="gone" />

(3) Get a handle to your WebView in your Activity and set its WebViewClient to pick up the Twitter callback (and not to kick off the web browser app at any time), as follows...

myWebView = (WebView)findViewById(R.id.myWebView);
myWebView.setWebViewClient(new WebViewClient()
{
  @Override
  public boolean shouldOverrideUrlLoading(WebView webView, String url)
  {
    if (url != null
        && url.startsWith("myapptwittercallback:///myapp"))
      handleTwitterCallback(url);
    else
      webView.loadUrl(url);
    return true;
  }
});

... Here you might also want to enable other settings of your WebView like saving form data and javascript execution (e.g. myWebView.getSettings().setJavaScriptEnabled(true);)

(4) Make your WebView visible and focused at the point you need to do Twitter authentication, as follows:

try
{
  twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer(
      myTwitterConsumerKey,
      myTwitterConsumerSecret);
  twitterRequestToken = twitter.getOAuthRequestToken(
      "myapptwittercallback:///myapp");
  myWebView.loadUrl(
      twitterRequestToken.getAuthenticationURL());
  myWebView.setVisibility(View.VISIBLE);
  myWebView.requestFocus(View.FOCUS_DOWN);
}
catch (TwitterException ex)
{
  Toast.makeText(
      this,
      "Login failed. Please try again.",
      Toast.LENGTH_SHORT).show();
}

... Remember to clear your WebView's history (i.e. myWebView.clearHistory() and set its visibility to "gone" in your handleTwitterCallback(String) method.

(5) Lastly, override your Activity's onBackPressed() method so that your WebView handles presses of the back button if its visible, as follows...

@Override
public void onBackPressed()
{
  if (myWebView.getVisibility() == View.VISIBLE)
  {
    if (myWebView.canGoBack())
    {
      myWebView.goBack();
      return;
    }
    else
    {
      myWebView.setVisibility(View.GONE);
      return;
    }
  }
  super.onBackPressed();
}

That's it! You should now have Twitter integration in your app and the user should never leave your app in doing Twitter authentication.

15 comments:

Anonymous said...

Hi.

Thanks man for such a good article. Had been banging my head around ACTION_VIEW and new views for 2 weeks without any luck.

The results were simply not stable enough.

Anonymous said...

Hey thank you for your codes. I'm stuck at handleTwitterCallback(url). Can you show me an example of what should be in it? Thanks

adil said...

@honeyhong, here's some things I do in my handleTwitterCallback(String url) method...

(1)

webView.clearHistory();

(2)

Uri uri = Uri.parse(url);

(3)

String oauthVerifier = uri.getQueryParameter("oauth_verifier");

(4)

AccessToken accessToken = twitter.getOAuthAccessToken(myTwitterRequestToken, oauthVerifier);

(5)

twitter.setOAuthAccessToken(accessToken);

That's it at its most basic. Be sure to add null- and exception- handling. And calls (4) and (5) will need to be in an AsyncTask (i.e. not called from the main ui thread). Hope that helps!

Anonymous said...

Hi,

my app crashes @ this line
request = twitter.getOAuthRequestToken(
"myapptwittercallback:///myapp" );

I don't understand what I have to write instead of "myapptwittercallback:///myapp");

what doe's this line mean?

THX

adil said...

Hi Anonymous,

Most likely your app is crashing because you're calling the 'Twitter.getOAuthRequestToken(...)' method in the main ui thread of your activity and not in a background/async task. Call it instead in an 'AsyncTask.doInBackground(...)' method and it should work fine.

And your callback url can be anything really as long as it's the same in all places within your app.

Anonymous said...

Hi,

sorry for my bad english.

Thank you for the fast answer. I am trying to run twitter since 3 weeks for my application and nothing works.

Is it possible to explain this more detail?
Do I have to implement a new Class which extends from AsyncTask<> and write the doInBackgroundMethod() like this:
protected RequestToken doInBackground(Twitter...params)
{
return
twitter.getOAuthRequestToken( "myapptwittercallback:///myapp" );
}

and then instead of the call I do:
request = AsyncClass.doInBackground()?

THX

Anonymous said...

hi again,

so it's done!

I thank you so much man now it work's. That's all a little bit complicated ^^ but it works.

THANK YOU SO MUCH

Greetings from Germany.

Max Flats said...

Adil..this is the best tutorial ever! I found out the reason why twitter4j was not working was cos getOauthAccessToken was running from main thread.

Besides using a webview is better for UX flow... thanks its work for my app and I'll let you know when the app is done!

Ron said...

Thanks for this article! Almost got stuck with the popup browser! :)

Unknown said...

Hi.. I got stuck with that handleTwitterCallback (url)..

Sorry coz i'm still new to progamming..

Can u explain that part further?



private void handleTwitterCallBack(String url){
myWebView.clearHistory();
Uri uri = Uri.parse(url);
final String veri = uri.getQueryParameter(URL_TWITTER_OAUTH_VERIFIER);
new AsyncTask(){

@Override
protected String doInBackground(String... arg0) {
// TODO Auto-generated method stub
try{
AccessToken at = twitter.getOAuthAccessToken(requestToken,veri);

twitter.setOAuthAccessToken(at);
}catch(TwitterException e){
System.out.println("EEEE :"+e.getErrorMessage());
}
return null;
}

}.execute(url);
myWebView.setVisibility(View.GONE);

}






This is my method..
Is it wrong? Can u help me to fix it if it's wrong?

Big thanks from me..


Mike Storms said...

Brilliant, man!
Concise and well explained.
Thank you very, very much.

Alvin Varghese said...

Alvin Varghese :
Please take look at this question.

http://stackoverflow.com/questions/21108320/android-app-not-signing-in-to-twitter

adil said...

Alvin, I've left an answer for you on Stack Overflow.

Anonymous said...

A truly useful post - many thanks!

In response to some other questions, here is what I did:

private void handleTwitterCallback (String url) {

myWebView.clearHistory();
myWebView.setVisibility(View.GONE);

Uri uri = Uri.parse(url);

String oauthVerifier = uri.getQueryParameter("oauth_verifier");
new HandleTwitterCallback_AsyncCommand(oauthVerifier).execute();
}

public class HandleTwitterCallback_AsyncCommand extends AsyncTask {


private String mOauthVerifier = "";

public HandleTwitterCallback_AsyncCommand(String oauthVerifier) {
super();

mOauthVerifier = oauthVerifier;
}

@Override
protected Void doInBackground(Void... params) {
try {
// This must not run on the main UI thread...
AccessToken accessToken = mTwitter.getOAuthAccessToken(mReqToken, mOauthVerifier);
mTwitter.setOAuthAccessToken(accessToken);
saveAccessToken(accessToken);

mHandler.post(new Runnable(){
@Override
public void run() {
// We're authenticated - we can now tweet!
// This must run on the main UI thread... e.g. you can now post, if you want!
userLoggedIn();
}
});

} catch (TwitterException e) {
mHandler.post(new Runnable(){

@Override
public void run() {
String message = getString(R.string.socialmedia_twitter_login_error); // Twitter Login error, please try again later
Toast.makeText(MyActivity.this, message, Toast.LENGTH_SHORT).show();
}});

} catch (Exception e) {

}
return null;
}

}

Unknown said...

Would you like give me a complete code how to implement a webview load in a dialog ?