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:

  1. 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.

    ReplyDelete
  2. 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

    ReplyDelete
  3. @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!

    ReplyDelete
  4. 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

    ReplyDelete
  5. 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.

    ReplyDelete
  6. 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

    ReplyDelete
  7. 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.

    ReplyDelete
  8. 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!

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

    ReplyDelete
  10. 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..


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

    ReplyDelete
  12. Alvin Varghese :
    Please take look at this question.

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

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

    ReplyDelete
  14. 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;
    }

    }

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

    ReplyDelete