Advertisement

Creating a Twitter Client for Android: Tweeting, Retweeting, and Replying

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

In this series we are building a Twitter client for the Android platform using the Twitter4J library. This tutorial will focus on implementing tweeting, retweeting, and replying to tweets. We will create a new Activity for tweeting and replying, with retweet and reply buttons implemented for each tweet in the user's home timeline.


Also available in this series:

  1. Creating a Twitter Client for Android: Setup & Overview
  2. Creating a Twitter Client for Android: Building the Interface
  3. Creating a Twitter Client for Android: Creating a Timeline Database
  4. Creating a Twitter Client for Android: Retrieving Updates Using a Service
  5. Creating a Twitter Client for Android: Tweeting, Retweeting, and Replying

In the first four tutorials we:

  • Registered the app with Twitter.
  • Imported the Twitter4J library.
  • Handled user authentication.
  • Built the user interface using XML resources.
  • Created an SQLite database to store the tweets for the user's home timeline.
  • Mapped the data to the visible Views using an Adapter.
  • Fetched new updates periodically using a Service and Broadcast.

Step 1: Create a Tweet Activity

Our app is going to have a second Activity in addition to the main timeline screen. This new Activity is for sending Tweets. Inside it, the user can enter text to send a tweet from their Twitter account. The user may enter the Tweet Activity in two ways - by pressing the Tweet button on the app's main screen or by pressing a reply button within the timeline.


If the user presses the Tweet button, they will be presented with an empty text-field for entering their tweet, with a send button to go ahead and send it.


If the user presses the reply button within a timeline tweet, they will also be presented with the text-field, but the reply-to username will already be populated within the field. The reply must also be sent using the ID of the tweet being replied to, which we will implement as well.

Create a new class in your Android project, naming it "NiceTweet" to match the name you included in your Manifest file in the first tutorial. Alter the class declaration as follows:

 
public class NiceTweet extends Activity implements OnClickListener {

You will need the following Android imports:

 
import android.app.Activity; 
import android.content.SharedPreferences; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.EditText; 
import android.widget.LinearLayout;

Plus these Twitter4J imports:

 
import twitter4j.StatusUpdate; 
import twitter4j.Twitter; 
import twitter4j.TwitterException; 
import twitter4j.TwitterFactory; 
import twitter4j.conf.Configuration; 
import twitter4j.conf.ConfigurationBuilder;

Include the following instance variables within your class declaration:

 
	/**shared preferences for user twitter details*/ 
private SharedPreferences tweetPrefs; 
	/**twitter object**/ 
private Twitter tweetTwitter; 
 
	/**twitter key*/ 
public final static String TWIT_KEY = "your key"; 
	/**twitter secret*/ 
public final static String TWIT_SECRET = "your secret"; 
 
	/**the update ID for this tweet if it is a reply*/ 
private long tweetID = 0; 
	/**the username for the tweet if it is a reply*/ 
private String tweetName = "";

We will use the Preferences, Twitter, key and secret variables to access the Twitter4J methods for tweeting from the user account. Alter the key and secret to suit your own. The final two variables are for replies. When the user presses the reply button within a tweet, we are going to pass the ID of the tweet being replied to, together with the Twitter screen-name of the account we are replying to. We will store these in the two instance variables.

Let's implement the Activity create method:

 
/* 
 * onCreate called when activity is created 
 */ 
@Override 
public void onCreate(Bundle savedInstanceState) { 
	super.onCreate(savedInstanceState); 
		//set tweet layout 
	setContentView(R.layout.tweet);   
}

All we do here is set the tweet layout we defined in XML earlier. Next implement the resume method, which is called whenever the Activity becomes visible to the user:

 
/* 
 * Call setup method when this activity starts 
 */ 
@Override 
public void onResume() { 
	super.onResume(); 
		//call helper method 
	setupTweet(); 
}

We are going to provide the specified helper method to set the Activity up for user interaction.


Step 2: Prepare to Tweet

Add the "setupTweet" method to your class as follows:

 
/** 
 * Method called whenever this Activity starts 
 * - get ready to tweet 
 * Sets up twitter and onClick listeners 
 * - also sets up for replies 
 */ 
private void setupTweet() { 
	//prepare to tweet 
 
}

Inside this method, we need to prepare the class to send ordinary tweets as well as replies. First let's use the Shared Preferences to instantiate our Twitter object:

 
	//get preferences for user twitter details 
tweetPrefs = getSharedPreferences("TwitNicePrefs", 0); 
         
       	//get user token and secret for authentication 
String userToken = tweetPrefs.getString("user_token", null); 
String userSecret = tweetPrefs.getString("user_secret", null); 
 
	//create a new twitter configuration usign user details 
Configuration twitConf = new ConfigurationBuilder() 
	.setOAuthConsumerKey(TWIT_KEY) 
	.setOAuthConsumerSecret(TWIT_SECRET) 
	.setOAuthAccessToken(userToken) 
	.setOAuthAccessTokenSecret(userSecret) 
	.build(); 
 
	//create a twitter instance 
tweetTwitter = new TwitterFactory(twitConf).getInstance();

Here we create a Twitter object using the user's authentication information together with the developer key and secret for the application. When we take users to this Activity class, we are going to pass in the ID and username if a tweet is being replied to. We get these from the Intent:

 
	//get any data passed to this intent for a reply 
Bundle extras = getIntent().getExtras();

If the tweet is an ordinary update rather than a reply, there will be no extras. If there are extras, we know the tweet is a reply:

 
if(extras !=null) 
{ 
		//get the ID of the tweet we are replying to 
	tweetID = extras.getLong("tweetID"); 
		//get the user screen name for the tweet we are replying to 
	tweetName = extras.getString("tweetUser"); 
 
		//use the passed information 
 
}

Here we retrieve the ID and screen-name for the tweet to reply to. Next we use this information to add the username to the text-field, still inside the "if" statement:

 
	//get a reference to the text field for tweeting 
EditText theReply = (EditText)findViewById(R.id.tweettext); 
	//start the tweet text for the reply @username 
theReply.setText("@"+tweetName+" "); 
	//set the cursor to the end of the text for entry 
theReply.setSelection(theReply.getText().length());

We set the username as the first part of the tweet text, placing the cursor at the end so that the user can type their reply text straight away.


Next let's take care of cases where the tweet is not a reply:

 
else  
{ 
    	EditText theReply = (EditText)findViewById(R.id.tweettext); 
    	theReply.setText(""); 
}

Here we simply set the text to an empty String, again using the ID we gave the text-field in the layout XML. Now we can finish the "setupTweet" method by adding click listeners for the "send" button and the "home" button for returning to the main timeline screen:

 
	//set up listener for choosing home button to go to timeline 
LinearLayout tweetClicker = (LinearLayout)findViewById(R.id.homebtn); 
tweetClicker.setOnClickListener(this); 
         
	//set up listener for send tweet button 
Button tweetButton = (Button)findViewById(R.id.dotweet); 
tweetButton.setOnClickListener(this);

We again use the ID values we specified in our layout files.


Step 3: Detect Clicks

Remember that the Tweet Activity is going to contain a button to take users back to the home timeline as well as the button to send the tweet. Let's now add the "onClick" method to handle users clicking these buttons:

 
/** 
 * Listener method for button clicks 
 * - for home button and send tweet button 
 */ 
public void onClick(View v) { 
	//handle home and send button clicks 
 
}

First get a reference to the text-field:

 
EditText tweetTxt = (EditText)findViewById(R.id.tweettext);

Now we need to work out which button was clicked using switch and case statements:

 
	//find out which view has been clicked 
switch(v.getId()) { 
case R.id.dotweet: 
	//send tweet 
	 
	break; 
case R.id.homebtn: 
	//go to the home timeline 
 
	break; 
default: 
	break; 
}

Inside the "dotweet" case statement, before the break statement, implement sending a tweet as follows:

 
String toTweet = tweetTxt.getText().toString(); 
try { 
		//handle replies 
	if(tweetName.length()>0) 
		tweetTwitter.updateStatus(new StatusUpdate(toTweet).inReplyToStatusId(tweetID)); 
		 
		//handle normal tweets 
	else  
		tweetTwitter.updateStatus(toTweet); 
			 
		//reset the edit text 
	tweetTxt.setText(""); 
} 
catch(TwitterException te) { Log.e("NiceTweet", te.getMessage()); }

Here we check whether the tweet is a reply or not. The "if" statement executes if the tweet is a reply, including the text from the text-field and the ID of the status update we are replying to. The "else" statement takes care of sending ordinary tweets, in which only the text is passed as parameter. The try and catch blocks are necessary as we are attempting to connect to Twitter over the network. After sending the tweet, whether a reply or not, we set the text-field back to empty in preparation for the next tweet update.

For the "homebtn" case statement, simply set the text-field to an empty String:

 
tweetTxt.setText("");

Finally, after the switch statement but still inside the "onClick" method, finish the Activity so that it returns to the home timeline:

 
finish();

Whether the tweet is a reply or an ordinary update, we immediately take the user back to the main screen when it is sent, which we also do when they press the home button - the finish statement will execute in all three cases.


Step 4: Model Tweet Data for Retweets and Replies

To implement retweeting and replying, we need to store the tweet ID and user screen-name within the retweet and reply buttons for each tweet in the timeline. By storing this data within the button Views for retweet and reply, we will be able to detect which tweet is being retweeted or replied to when the user presses a button.

Because the information we need to store comprises a number and some text, we will create a class to model it. Create a new class in your project and name it "StatusData". Your new class should begin as follows:

 
public class StatusData {

Inside the class, add instance variables for the tweet ID and username:

 
/**tweet ID*/ 
private long tweetID; 
 
/**user screen name of tweeter*/ 
private String tweetUser;

The ID is modeled as a long, with the screen-name a text String. Add a constructor method to the class:

 
/** 
 * Constructor receives ID and user name 
 * @param ID 
 * @param screenName 
 */ 
public StatusData(long ID, String screenName) { 
		//instantiate variables 
	tweetID=ID; 
	tweetUser=screenName; 
}

The method simply instantiates the two variables. Next add a public method so that we can retrieve the tweet ID elsewhere:

 
/** 
 * Get the ID of the tweet 
 * @return tweetID as a long 
 */ 
public long getID() {return tweetID;}

Then add a method to return the screen-name:

 
/** 
 * Get the user screen name for the tweet 
 * @return tweetUser as a String 
 */ 
public String getUser() {return tweetUser;}

These methods will allow us to retrieve this information when users click the retweet or reply button for a particular tweet.


Step 5: Extend Binding for Retweets and Replies

Now we need to extend the code in the Adapter class we created ("UpdateAdapter"). In the "bindView" method, we tailored the mapping of data to the user interface Views. Now we will add further processing to include the tweet data within each retweet and reply button. Before the end of your "bindView" method, begin as follows:

 
	//get the status ID 
long statusID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); 
	//get the user name 
String statusName = cursor.getString(cursor.getColumnIndex("user_screen"));

Remember that the "bindView" method is passed a Cursor object for traversing the data. Here we use the Cursor to retrieve the long ID value and String username for the current tweet. You will need the following additional import:

 
import android.provider.BaseColumns;

Now we will instantiate an object of our new StatusData class, passing the tweet data to the constructor method:

 
	//create a StatusData object to store these 
StatusData tweetData = new StatusData(statusID, statusName);

The StatusData object contains everything necessary to send a retweet or reply for the tweet in question. We will now attach a reference to this object within the retweet and reply buttons for the tweet, so that we can access the information following user clicks. We use the "setTag" method:

 
	//set the status data object as tag for both retweet and reply buttons in this view 
row.findViewById(R.id.retweet).setTag(tweetData); 
row.findViewById(R.id.reply).setTag(tweetData);

The "bindView" method also receives a parameter representing the row View in which the tweet is going to be displayed. If you look back at the update XML layout file, you will see that these two ID values are included for the buttons. We set the tag in each button to reflect the StatusData object holding the ID and username for the tweet displayed. Now we need to setup button clicks for these:

 
	//setup onclick listeners for the retweet and reply buttons 
row.findViewById(R.id.retweet).setOnClickListener(tweetListener); 
row.findViewById(R.id.reply).setOnClickListener(tweetListener);

Here we specify a click listener to handle button clicks. Inside the listener method, we will be able to retrieve the StatusData objects of any retweet and reply buttons clicked. We are also going to allow users to click the username for a tweet in order to open the user profile in the Web browser. Add a click listener to that View here as well:

 
	//setup  onclick for the user screen name within the tweet 
row.findViewById(R.id.userScreen).setOnClickListener(tweetListener);

This will allow us to link into the Twitter Web interface so that users can access functions we have not provided within the app itself.


Step 6: Handle Button and Username Clicks

In your UpdateAdapter class, create an "onClickListener" to handle clicks, using the name "tweetListener" to match what we specified in the "bindView" method:

 
/** 
 * tweetListener handles clicks of reply and retweet buttons 
 * - also handles clicking the user name within a tweet 
 */ 
private OnClickListener tweetListener = new OnClickListener() { 
	//onClick method 
	public void onClick(View v) { 
 
	} 
};

Add the following additional imports to the Adapter class:

 
import android.view.View.OnClickListener; 
import android.content.Intent; 
import android.content.SharedPreferences; 
import android.widget.Toast; 
import android.net.Uri; 
import twitter4j.Twitter; 
import twitter4j.TwitterException; 
import twitter4j.TwitterFactory; 
import twitter4j.conf.Configuration; 
import twitter4j.conf.ConfigurationBuilder;

Inside the "onClick" method we will implement clicks of both buttons plus the username. To detect which one has been clicked, add a switch statement:

 
//which view was clicked 
switch(v.getId()) { 
		//reply button pressed 
	case R.id.reply: 
		//implement reply 
 
		break; 
		//retweet button pressed 
	case R.id.retweet: 
		//implement retweet 
 
		break; 
		//user has pressed tweet user name 
	case R.id.userScreen: 
		//implement visiting user profile 
 
		break; 
	default: 
		break; 
}

Inside the reply case statement, we start the Tweet Activity class, passing the reply data so that the reply ID and username can be included when sending the tweet:

 
	//create an intent for sending a new tweet 
Intent replyIntent = new Intent(v.getContext(), NiceTweet.class); 
	//get the data from the tag within the button view 
StatusData theData = (StatusData)v.getTag(); 
	//pass the status ID 
replyIntent.putExtra("tweetID", theData.getID()); 
	//pass the user name 
replyIntent.putExtra("tweetUser", theData.getUser()); 
	//go to the tweet screen 
v.getContext().startActivity(replyIntent);

Notice that we retrieve the tag for the pressed View, casting it as a StatusData object. We then call the public methods we provided within the StatusData class to return the tweet ID and user screen-name. We pass these to the Tweet Activity as extras, then start the Activity. At this point the user will be taken to the Tweet screen where the reply data will be used to implement replying to the correct tweet.


In the retweet case statement, we will retweet the relevant tweet directly, using the Twitter4J methods. First instantiate a Twitter object using the Shared Preferences plus your developer key and secret for the app:

 
	//get context 
Context appCont = v.getContext(); 
	//get preferences for user access 
SharedPreferences tweetPrefs = appCont.getSharedPreferences("TwitNicePrefs", 0); 
String userToken = tweetPrefs.getString("user_token", null); 
String userSecret = tweetPrefs.getString("user_secret", null); 
 
	//create new Twitter configuration 
Configuration twitConf = new ConfigurationBuilder() 
	.setOAuthConsumerKey(TWIT_KEY) 
	.setOAuthConsumerSecret(TWIT_SECRET) 
	.setOAuthAccessToken(userToken) 
	.setOAuthAccessTokenSecret(userSecret) 
	.build(); 
 
	//create Twitter instance for retweeting 
Twitter retweetTwitter = new TwitterFactory(twitConf).getInstance();

This is the same technique we have used to instantiate the Twitter class before. We can now use the Twitter object to send the retweet. First we need to retrieve the StatusData object from the button that has been clicked:

 
	//get tweet data from view tag 
StatusData tweetData = (StatusData)v.getTag();

Now we can attempt to retweet the update in a try block:

 
try  
{ 
		//retweet, passing the status ID from the tag 
	retweetTwitter.retweetStatus(tweetData.getID()); 
 
} 
catch(TwitterException te) {Log.e(LOG_TAG, te.getMessage());}

All the Twitter object needs to send a retweet is the ID of the original tweet. However, let's give the user confirmation that their retweet was sent, still inside the try block:

 
	//confirm to use 
CharSequence text = "Retweeted!"; 
int duration = Toast.LENGTH_SHORT; 
Toast toast = Toast.makeText(appCont, text, duration); 
toast.show();

You can of course alter the message if you like. This is what it looks like:


Now let's allow the user to visit a Twitter profile page in the Web browser by clicking the screen-name within the current tweet, inside the "userScreen" case statement:

 
	//get the user screen name 
TextView tv = (TextView)v.findViewById(R.id.userScreen); 
String userScreenName = tv.getText().toString(); 
	//open the user's profile page in the browser 
Intent browserIntent = new Intent(Intent.ACTION_VIEW,  
	Uri.parse("http://twitter.com/"+userScreenName)); 
v.getContext().startActivity(browserIntent);

Here we retrieve the user name as a text String from the View itself, which displays the screen-name as a String anyway. We build this into a Twitter profile page address, parsing it as a URI and instructing the browser to open it.


Step 7: Implement Moving from Home to Tweet

Remember that our main app Activity displays a button to take users straight to the Tweet screen. Let's implement that now. In your main Activity class, add the following inside your "setupTimeline" method, anywhere after you set the main content view:

 
	//setup onclick listener for tweet button 
LinearLayout tweetClicker = (LinearLayout)findViewById(R.id.tweetbtn); 
tweetClicker.setOnClickListener(this);

The Tweet button ID matches what we included in the main timeline XML layout file. The main Activity class is going to handle clicks of the Tweet button. If you look at your "onClick" method within the main Activity ("TwitNiceActivity" if you used the name in the first tutorial), you should see a switch statement with one case statement for the "signin" button. Add a second case statement for the Tweet button as follows (before the default statement):

 
	//user has pressed tweet button 
case R.id.tweetbtn: 
		//launch tweet activity 
	startActivity(new Intent(this, NiceTweet.class)); 
	break;

Here we simply start the Tweet Activity. We do not need to pass any information to the Activity as in this case the user is simply launching it to send an ordinary tweet.


That's It!

That's our Twitter app complete! Run the app on an emulator or device to see it function. On first run you will of course have to consent to let the app use your Twitter account. You can optionally create a separate Twitter account to test the app with, rather than using your normal account. After authorization, you should be presented with the home timeline representing the most recent tweets from the accounts you follow. Test tweeting, retweeting and replying, as well as making sure your timeline is automatically updating at your chosen interval.


Advanced Topics

In this tutorial series we have created a basic Twitter client for Android. There are many ways in which you could enhance and improve upon the app, such as including the ability to view direct messages or to view user profiles within the app rather than having to use the Web browser. You could also make mentions of users within tweets clickable (i.e. linking any text String preceded by "@" to the profile page for that user). A similar process would allow you to support hashtags. More advanced Twitter clients also display conversations when selecting an individual tweet, showing replies in chronological order.

In terms of the application implementation, there are also enhancements you could consider. For example, in case the user has low connectivity, you could implement downloading the profile images as a background process. You could also implement some form of image caching to maximize on efficiency. Rather than the app automatically refreshing the ListView with new Tweets, you could implement a button at the top of the timeline, for users to control the display, with the button indicating how many new tweets are available. Finally, you could improve efficiency by running the Service in a separate Thread.


Thanks For Reading

Hope you've enjoyed this series on Creating a Twitter Client for the Android platform! As well as learning how to connect your apps to Twitter, you now have experience of using an external library plus a variety of Android platform resources such as Databases, Adapters, Services and Broadcasts. These are all key skills your future Android projects will benefit from. The downloadable source code contains additional notes you may find helpful.

Advertisement