Monday, June 6, 2011

Twitter integration in android application

Introduction

The goal of this article is to get twitter integration up & running from your Android app in 30 minutes. The guide will show you how to:
setup a twitter test account.
register a twitter application.
authenticate the user in your Android application.
have the user send tweets from your Android application.
The goal of this article is to get twitter integration up & running from your Android app in 30 minutes. The guide will show you how to:
  • setup a twitter test account.
  • register a twitter application.
  • authenticate the user in your Android application.
  • have the user send tweets from your Android application.
twitter_integration_android_00

This guide is accompanied  by a sample application that’s available in Github in the AndroidTwitterSample repository. To import this project in Eclipse, I suggest using the EGit plugin that can be installed via the Main P2 Repository located at http://download.eclipse.org/egit/updates.
Before running this project, make sure you change the com.ecs.android.sample.twitter.Constants file to include your consumer key and consumer secret. (see subsequent section).
Once you have sample application up & running, you can copy the relevant classes into your projects to have Twitter up & running.

Twitter uses the OAuth protocol to authorize your android application to send tweets on behalf of the end-user. The end-user will need to authenticate against Twitter (meaning that your application will not capture the twitter username / password). Once the user has authorized access, you’ll be able to send tweets on behalf of the user. We’ll use signpost library to handle the OAuth communication, and the Twitter4J library to handle the Twitter specific interactions (sending tweets).



Setting up the Twitter account and application.

We’ll start by setting up a test-account on Twitter that we’ll use in our Android application. Goto the Twitter signup page and create an account. You’ll receive an  email from Twitter to confirm your account. You can skip the friends import as it will only be used for testing purposes.

twitter_integration_android_01

Now that we have the Twitter account setup, we need to define an application. Go to the Twitter Application Registration page, and register an application. The application that your register here is required to perform Twitter interaction from your Android application. The Twitter application will have a consumer key and consumer secret associated with it that we’ll use in our Android application.
Fill in the required fields like you can see in the screenshot below:


twitter_integration_android_02

Once the application is registered, you’ll receive the following information associated with your application :

Consumer key
************************ (masked)

Consumer secret
************************ (masked)

Request token URL

https://api.twitter.com/oauth/request_token

Access token URL

https://api.twitter.com/oauth/access_token

Authorize URL

https://api.twitter.com/oauth/authorize

Registered OAuth Callback URL

http://someurl.com

This is all the information we need to start integrating Twitter in our Android application.
Note : The callback URL specified here is just a required field that we need to fill in, but is not used in our application. Instead, we define our own callback URL that we’ll pass on when authenticating the user.



The sample application

The sample application is available in Github in the AndroidTwitterSample repository. Before running this project, make sure you change the com.ecs.android.sample.twitter.Constants file to include your consumer key and consumer secret. The application provides you with an end-to-end example on how to authenticate against Twitter and send tweets on behalf or the authenticated user.
The sample project has a dependency towards the following libraries :
  • signpost-commonshttp4-1.2.1.1.jar
  • signpost-core-1.2.1.1.jar
  • httpclient-4.0.1.jar
  • twitter4j-core-2.1.11
Dev note : You’ll need to include these libraries into your own project if you want to enable the Twitter integration.

The sample application contains 1 main activity with:
  • a status message, indicating if you’re logged into Twitter.
  • a Tweet button, used to send a Tweet. When not authenticated, the application will redirect you to the Twitter loging page.
  • a Clear Credentials button, removing all saved credentials, forcing you to login next time you want to send a Tweet.
twitter_integration_android_03



The Constants file

The Android project contains a Constants file containing the following information we received from Twitter when we setup our application.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Constants {
 
 public static final String CONSUMER_KEY = "<FILL IN YOUR CONSUMER KEY FROM TWITTER HERE>";
 public static final String CONSUMER_SECRET= "<FILL IN YOUR CONSUMER SECRET FROM TWITTER HERE>";
 
 public static final String REQUEST_URL = "http://api.twitter.com/oauth/request_token";
 public static final String ACCESS_URL = "http://api.twitter.com/oauth/access_token";
 public static final String AUTHORIZE_URL = "http://api.twitter.com/oauth/authorize";
 
 final public static String CALLBACK_SCHEME = "x-latify-oauth-twitter";
 final public static String CALLBACK_URL = CALLBACK_SCHEME + "://callback";
 
}

Dev note : If you want to integrate Twitter in your own app, I suggest making similar Constants available throughout your application.

What we have defined in the Constants class:

twitter_integration_android_04



The tweet button

The code behind the Tweet button is implemented  like this:

1
2
3
4
5
6
7
8
9
10
11
tweet.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
     if (TwitterUtils.isAuthenticated(prefs)) {
      sendTweet();
     } else {
Intent i = new Intent(getApplicationContext(), PrepareRequestTokenActivity.class);
i.putExtra("tweet_msg",getTweetMsg());
startActivity(i);
     }
    }
});

From a user experience, we opted to not have a seperate login button, but just have a single Tweet button, and encapsulate the authentication logic inside the Tweet login.
Basically, it performs a check to see if the user has already authenticated to Twitter.
  • If not, he’ll be redirected to the twitter login page by popping a browser. Once the user has authenticated, he’ll authorize the Android application to send tweets on the users behalf and the tweet will be sent (notice how we pass the tweet msg to the Intent when starting  the PrepareRequestTokenActivity activity).
  • If the user was already authenticated before, the tweet will be sent immediately.


Authenticating the user

We’ll start with the first case, where the user hasn’t authenticated to Twitter yet. This is the most complex part, as it involves implementing the OAuth flow to finally retrieve an access token.
This complete OAuth implementation is done in the OAuthRequestTokenTask and the PrepareRequestTokenActivity classes.

Before the user can authorize our application to send Tweets on his behalf, we’ll first need to redirect the user to the Twitter login page. This is done through the PrepareRequestTokenActivity. This will kick in the OAuth interactions required for your application to send tweets on behalf of the logged in user.

Dev note: I suggest copying these 2 classes into your own project, as they have been setup in a generic way.

The following table shows you in detail what each class does:

twitter_integration_android_04a

The PrepareRequestTokenActivity activity sets up our OAuth consumer and provider (using the Signpost library), and starts an Asynchronous task called OAuthRequestTokenTask.

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
    try {
     this.consumer = new CommonsHttpOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
        this.provider = new CommonsHttpOAuthProvider(Constants.REQUEST_URL,Constants.ACCESS_URL,Constants.AUTHORIZE_URL);
    } catch (Exception e) {
     Log.e(TAG, "Error creating consumer / provider",e);
 }
 
       Log.i(TAG, "Starting task to retrieve request token.");
 new OAuthRequestTokenTask(this,consumer,provider).execute();
}

This asynchronous task just starts an Intent that launches a browser to authenticate the user against Twitter. At this point, our Android application gives control to Twitter. The user enters his username/password, and Twitter will do the authentication. Notice how we also specify a callback URL. Twitter will redirect to this callback URL after the user has properly authenticated against twitter. In our case, the callback URL is x-oauthflow-twitter://callback. This callback is required to give back the control to our Android application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected Void doInBackground(Void... params) {
 
 try {
  Log.i(TAG, "Retrieving request token from Google servers");
  final String url = provider.retrieveRequestToken(consumer, Constants.OAUTH_CALLBACK_URL);
  Log.i(TAG, "Popping a browser with the authorize URL : " + url);
  Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | 
     Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_FROM_BACKGROUND);
  context.startActivity(intent);
 } catch (Exception e) {
  Log.e(TAG, "Error during OAUth retrieve request token", e);
 }
 
 return null;
}



The twitter login page

twitter_integration_android_05

In our AndroidManifest.xml, The PrepareRequestTokenActivity, responsible for popping the browser has an intent filter defined with a scheme and host that corresponds to our callback.

1
2
3
4
5
6
7
8
<activity android:name=".PrepareRequestTokenActivity" android:launchMode="singleTask">>
 <intent-filter>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="x-oauthflow-twitter" android:host="callback" />
 </intent-filter>
</activity>

Dev note: You’ll need to include this activity into your own project if you want to enable the Twitter integration.

Due to the fact that we have the intent filter defined we’ll be able to intercept the callback that Twitters sends. So after the user authenticated, Twitter sends a redirect to our callback URL, causing our onNewIntent method to kick in. At this point, we can continue with the OAuth flow and retrieve the access token using the RetrieveAccessTokenTask.

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onNewIntent(Intent intent) {
 super.onNewIntent(intent);
 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
 final Uri uri = intent.getData();
 if (uri != null && uri.getScheme().equals(Constants.OAUTH_CALLBACK_SCHEME)) {
  Log.i(TAG, "Callback received : " + uri);
  Log.i(TAG, "Retrieving Access Token");
  new RetrieveAccessTokenTask(this,consumer,provider,prefs).execute(uri);
  finish();
 }
}

The RetrieveAccessTokenTask is responsible for capuring the access token, required to do communication with Twitter.
It basically extracts the access token from the callback URL, passed on to it from the previous code snippet. We then store the access token and secret in our shared preferences, so that the user doesn’t need to login to Twitter again, not even after restarting the application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
protected Void doInBackground(Uri...params) {
 final Uri uri = params[0];
 final String oauth_verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
 
 try {
  provider.retrieveAccessToken(consumer, oauth_verifier);
 
  final Editor edit = prefs.edit();
  edit.putString(OAuth.OAUTH_TOKEN, consumer.getToken());
  edit.putString(OAuth.OAUTH_TOKEN_SECRET, consumer.getTokenSecret());
  edit.commit();
 
  String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
  String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
 
  consumer.setTokenWithSecret(token, secret);
  context.startActivity(new Intent(context,AndroidTwitterSample.class));
 
  executeAfterAccessTokenRetrieval();
 
  Log.i(TAG, "OAuth - Access Token Retrieved");
 
 } catch (Exception e) {
  Log.e(TAG, "OAuth - Access Token Retrieval Error", e);
 }
 
 return null;
}

Notice the executeAfterAccessTokenRetrieval method, that will be executed after having retrieved the access token. We’ll use this hook to actually send the message now.
It basically extracts the tweet message from the Intent that was passed along in the beginning of the OAuth flow (when clicking the Tweet button).

1
2
3
4
5
6
7
8
private void executeAfterAccessTokenRetrieval() {
 String msg = getIntent().getExtras().getString("tweet_msg");
 try {
  TwitterUtils.sendTweet(prefs, msg);
 } catch (Exception e) {
  Log.e(TAG, "OAuth - Error sending to Twitter", e);
 }
}



Sending the Tweet

Sending the actual Tweet is via a background thread like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void sendTweet() {
 Thread t = new Thread() {
        public void run() {
 
         try {
          TwitterUtils.sendTweet(prefs,getTweetMsg());
          mTwitterHandler.post(mUpdateTwitterNotification);
   } catch (Exception ex) {
    ex.printStackTrace();
   }
        }
 
    };
    t.start();
}

In order to provide feedback to the user (Toast message), we perform a post to a handler defined in our main Activity.

1
2
3
4
5
6
7
private final Handler mTwitterHandler = new Handler();
 
   final Runnable mUpdateTwitterNotification = new Runnable() {
       public void run() {
        Toast.makeText(getBaseContext(), "Tweet sent !", Toast.LENGTH_LONG).show();
       }
   };

The actual sending of the Tweet is done through the Twitter4J library, where we retrieve the access token and secret. We set the consumer key, consumer secret and access token on our Twitter object, and call the updateStatus method to send our Tweet.

1
2
3
4
5
6
7
8
9
10
public static void sendTweet(SharedPreferences prefs,String msg) throws Exception {
 String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
 String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
 
 AccessToken a = new AccessToken(token,secret);
 Twitter twitter = new TwitterFactory().getInstance();
 twitter.setOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
 twitter.setOAuthAccessToken(a);
       twitter.updateStatus(msg);
}

After making this call, your Tweet will be visible on your Twitter page:

twitter_integration_android_06

12 comments:

  1. I ran the sample app in the eclipse and is also connected properly . Its also authenticated and say status as Logged in to twitter:True but i cannot send the tweet message when i click tweet button its not responding . Its staying as it is . Could you please help on this.

    ReplyDelete
    Replies
    1. You want to change the Read write access for the application in Twitter development site.

      Delete
  2. same problem has happened to my application

    ReplyDelete
  3. I have found the missing part, default access type should be read & write. If there is only read access, above situation happens

    ReplyDelete
  4. Thanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.

    ReplyDelete
  5. Rather than opening a browser page (with help of callback url) after successful authentication process, I would like to open an activity of my project. For this, what should I set in the callback url? I had also tried keeping callback_url blank but then where do I set which activity should be opened after the page "redirecting back to your application"?

    ReplyDelete
  6. Got the solution so sharing here if someone wants it in future:

    I had to set twitter_callback_url as "TweetActivity://connect"

    where "TweetActivity" is the activity which i wanted to open after successful authentication.

    ReplyDelete
    Replies
    1. Thanks Anonymous for sharing your knowledge

      Delete
  7. Hello,

    I get the following error, anyone can help please:

    E/AndroidRuntime(9671): FATAL EXCEPTION: main
    E/AndroidRuntime(9671): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.newtest/com.example.newtest}: java.lang.ClassNotFoundException: com.example.newtest
    E/AndroidRuntime(9671): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2024)
    E/AndroidRuntime(9671): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125)
    E/AndroidRuntime(9671): at android.app.ActivityThread.access$600(ActivityThread.java:140)
    E/AndroidRuntime(9671): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1227)
    E/AndroidRuntime(9671): at android.os.Handler.dispatchMessage(Handler.java:99)
    E/AndroidRuntime(9671): at android.os.Looper.loop(Looper.java:137)
    E/AndroidRuntime(9671): at android.app.ActivityThread.main(ActivityThread.java:4898)
    E/AndroidRuntime(9671): at java.lang.reflect.Method.invokeNative(Native Method)
    E/AndroidRuntime(9671): at java.lang.reflect.Method.invoke(Method.java:511)
    E/AndroidRuntime(9671): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
    E/AndroidRuntime(9671): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
    E/AndroidRuntime(9671): at dalvik.system.NativeStart.main(Native Method)
    E/AndroidRuntime(9671): Caused by: java.lang.ClassNotFoundException: com.example.newtest
    E/AndroidRuntime(9671): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
    E/AndroidRuntime(9671): at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
    E/AndroidRuntime(9671): at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
    E/AndroidRuntime(9671): at android.app.Instrumentation.newActivity(Instrumentation.java:1057)
    E/AndroidRuntime(9671): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2015)

    ReplyDelete
  8. Thanks for posting useful information.You have provided an nice article, Thank you very much for this one. And i hope this will be useful for many people.. and i am waiting for your next post keep on updating these kinds of knowledgeable things...Really it was an awesome article...very interesting to read..
    please sharing like this information......
    Android training in chennai
    Ios training in chennai

    ReplyDelete
  9. This article is very much helpful and i hope this will be an useful information for the needed one. Keep on updating these kinds of

    informative things...
    Mobile App Development Company
    Mobile App Development Company in India
    Mobile App Development Companies

    ReplyDelete