- Reacting to the Network State
- Using SMS
- Using Web Content
- Social Networking
Social Networking
Twitter is a social networking and microblogging service that enables its users to send and read messages known as tweets. Twitter is described as the “SMS of the Internet,” and indeed, each tweet cannot exceed 140 characters (although links are converted to shorter links and not counted against the 140-character limit). Twitter users can follow other people’s tweets or be followed by others.
Recipe: Reading the Owner Profile
Starting with API Level 14 (Ice Cream Sandwich), developers are able to access the owner profile. This is a special contact that stores RawContact data. To read the owner profile of a device, the following permission must be added to the AndroidManifest.xml file:
<uses-permission android:name="android.permission.READ_PROFILE" />
The following enables access to profile data:
// sets the columns to retrieve for the owner profile - RawContact data String[] mProjection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // retrieves the profile from the Contacts Provider Cursor mProfileCursor = getContentResolver().query(Profile.CONTENT_URI,mProjection,null,null,null); // Set the cursor to the first entry (instead of -1) boolean b = mProfileCursor.moveToFirst(); for(int i = 0, length = mProjection.length;i < length;i++) { System.out.println("*** " + mProfileCursor.getString(mProfileCursor.getColumnIndex(mProjection[i]))); }
Note that where System.out.println() is used is the place where logic can be inserted to process the profile information. It is also worth mentioning that the output will be shown in LogCat, even though it is not a method from Log.*.
Recipe: Integrating with Twitter
Some third-party libraries exist to assist in integrating Twitter into Android applications (from http://dev.twitter.com/pages/libraries#java):
- Twitter4J by Yusuke Yamamoto—An open source, Mavenized, and Google App Engine-safe Java library for the Twitter API, released under the BSD license
- Scribe by Pablo Fernandez—OAuth module for Java, Mavenized, and works with Facebook, LinkedIn, Twitter, Evernote, Vimeo, and more
For this recipe, the Twitter4J library by Yusuke Yamamoto is used, which has documentation at http://twitter4j.org/en/javadoc/overview-summary.html. The recipe enables users to log in to Twitter by using OAuth and make a tweet.
Twitter has made changes to its authentication system that now require applications to register in order to access the public feed. To get started, an application has to be registered at https://dev.twitter.com/apps/new. During the registration process, OAuth public and private keys will be generated. They will be used in this recipe, so take note of them.
As this application will be accessing the Internet, it will need the INTERNET permission. There will also be a check to make sure that the device is connected to a network, so the ACCESS_NETWORK_STATE permission is also required. This is done by editing the AndroidManifest.xml file, as shown in Listing 10.12.
Listing 10.12. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cookbook.tcookbook" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.cookbook.tcookbook.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <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="oauth" android:host="tcookbook"/> </intent-filter> </activity> </application> </manifest>
For the layout of the application, everything will be put into the activity_main.xml file. This file will contain a button that is visible on page load and then several buttons, TextViews, and an EditText widget. Note that some of these will be hidden with android:visibility="gone". Listing 10.13 shows the contents of the activity_main.xml file.
Listing 10.13. res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > tools:context=".MainActivity" > <Button android:id="@+id/btnLoginTwitter" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Login with OAuth" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:layout_marginTop="30dip"/> <TextView android:id="@+id/lblUserName" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dip" android:layout_marginTop="30dip"/> <TextView android:id="@+id/lblUpdate" android:text="Enter Your Tweet:" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:visibility="gone"/> <EditText android:id="@+id/txtUpdateStatus" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dip" android:visibility="gone"/> <Button android:id="@+id/btnUpdateStatus" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Tweet it!" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:visibility="gone"/> <Button android:id="@+id/btnLogoutTwitter" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Logout/invalidate OAuth" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:layout_marginTop="50dip" android:visibility="gone"/> </LinearLayout>
One activity is used in the application, and two classes are used: one to help with connection detection and one to display an alert message when the wrong application OAuth keys are used.
In the main activity, several constants are set up for use. These include the OAuth Consumer key and Consumer secret. A connectivity check is run to make sure that the user can reach Twitter. Several OnClickListener classes are also registered to trigger logic such as login, logout, and update when clicked.
As Twitter handles authentication for the user, the information passed back is saved in application preferences and is checked again when the user attempts to log in to the application. An AsyncTask is also used to move any tweets made to a background thread.
Listing 10.14 shows the contents of the activity in full.
Listing 10.14. src/com/cookbook/tcookbook/MainActivity.java
package com.cookbook.tcookbook; import twitter4j.Twitter; import twitter4j.TwitterException; import twitter4j.TwitterFactory; import twitter4j.User; import twitter4j.auth.AccessToken; import twitter4j.auth.RequestToken; import twitter4j.conf.Configuration; import twitter4j.conf.ConfigurationBuilder; import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ActivityInfo; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.StrictMode; import android.text.Html; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { // Replace the following value with the Consumer key static String TWITTER_CONSUMER_KEY = "01189998819991197253"; // Replace the following value with the Consumer secret static String TWITTER_CONSUMER_SECRET = "616C6C20796F75722062617365206172652062656C6F6E6720746F207573"; static String PREFERENCE_NAME = "twitter _ oauth"; static final String PREF_KEY_OAUTH_TOKEN = "oauth_token"; static final String PREF_KEY_OAUTH_SECRET = "oauth_token_secret"; static final String PREF_KEY_TWITTER_LOGIN = "isTwitterLoggedIn"; static final String TWITTER_CALLBACK_URL = "oauth://tcookbook"; static final String URL_TWITTER_AUTH = "auth_url"; static final String URL_TWITTER_OAUTH_VERIFIER = "oauth_verifier"; static final String URL_TWITTER_OAUTH_TOKEN = "oauth_token"; Button btnLoginTwitter; Button btnUpdateStatus; Button btnLogoutTwitter; EditText txtUpdate; TextView lblUpdate; TextView lblUserName; ProgressDialog pDialog; private static Twitter twitter; private static RequestToken requestToken; private static SharedPreferences mSharedPreferences; private ConnectionDetector cd; AlertDialogManager adm = new AlertDialogManager(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // used for Android 2.3+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES_GINGERBREAD) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); cd = new ConnectionDetector(getApplicationContext()); if (!cd.isConnectingToInternet()) { adm.showAlertDialog(MainActivity.this, "Internet Connection Error", "Please connect to working Internet connection", false); return; } if(TWITTER_CONSUMER_KEY.trim().length() == 0 || TWITTER_CONSUMER_SECRET.trim().length() == 0){ adm.showAlertDialog(MainActivity.this, "Twitter OAuth tokens", "Please set your Twitter OAuth tokens first!", false); return; } btnLoginTwitter = (Button) findViewById(R.id.btnLoginTwitter); btnUpdateStatus = (Button) findViewById(R.id.btnUpdateStatus); btnLogoutTwitter = (Button) findViewById(R.id.btnLogoutTwitter); txtUpdate = (EditText) findViewById(R.id.txtUpdateStatus); lblUpdate = (TextView) findViewById(R.id.lblUpdate); lblUserName = (TextView) findViewById(R.id.lblUserName); mSharedPreferences = getApplicationContext().getSharedPreferences("MyPref", 0); btnLoginTwitter.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // Call login Twitter function loginToTwitter(); } }); btnUpdateStatus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String status = txtUpdate.getText().toString(); if (status.trim().length() > 0) { new updateTwitterStatus().execute(status); } else { Toast.makeText(getApplicationContext(), "Please enter status message", Toast.LENGTH_SHORT).show(); } } }); btnLogoutTwitter.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // Call logout Twitter function logoutFromTwitter(); } }); if (!isTwitterLoggedInAlready()) { Uri uri = getIntent().getData(); if (uri != null && uri.toString().startsWith(TWITTER_CALLBACK_URL)) { String verifier = uri.getQueryParameter(URL_TWITTER_OAUTH_VERIFIER); try { AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, verifier); Editor e = mSharedPreferences.edit(); 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()); btnLoginTwitter.setVisibility(View.GONE); lblUpdate.setVisibility(View.VISIBLE); txtUpdate.setVisibility(View.VISIBLE); btnUpdateStatus.setVisibility(View.VISIBLE); btnLogoutTwitter.setVisibility(View.VISIBLE); long userID = accessToken.getUserId(); User user = twitter.showUser(userID); String username = user.getName(); lblUserName.setText(Html.fromHtml("<b>Welcome " + username + "</b>")); } catch (Exception e) { Log.e("***Twitter Login Error: ",e.getMessage()); } } } } private void loginToTwitter() { if (!isTwitterLoggedInAlready()) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.setOAuthConsumerKey(TWITTER_CONSUMER_KEY); builder.setOAuthConsumerSecret(TWITTER_CONSUMER_SECRET); Configuration configuration = builder.build(); TwitterFactory factory = new TwitterFactory(configuration); twitter = factory.getInstance(); if(!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) { try { requestToken = twitter.getOAuthRequestToken(TWITTER_CALLBACK_URL); this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(requestToken.getAuthenticationURL()))); } catch (TwitterException e) { e.printStackTrace(); } } else { new Thread(new Runnable() { public void run() { try { requestToken = twitter.getOAuthRequestToken(TWITTER_CALLBACK_URL); MainActivity.this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(requestToken.getAuthenticationURL()))); } catch (TwitterException e) { e.printStackTrace(); } } }).start(); } } else { Toast.makeText(getApplicationContext(),"Already logged into Twitter", Toast.LENGTH_LONG).show(); } } class updateTwitterStatus extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { super.onPreExecute(); pDialog = new ProgressDialog(MainActivity.this); pDialog.setMessage("Updating to Twitter..."); pDialog.setIndeterminate(false); pDialog.setCancelable(false); pDialog.show(); } protected String doInBackground(String... args) { // Log.d("*** Text Value of Tweet: ",args[0]); String status = args[0]; try { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.setOAuthConsumerKey(TWITTER_CONSUMER_KEY); builder.setOAuthConsumerSecret(TWITTER_CONSUMER_SECRET); String access_token = mSharedPreferences.getString(PREF_KEY_OAUTH_TOKEN, ""); String access_token_secret = mSharedPreferences.getString(PREF_KEY_OAUTH_SECRET, ""); AccessToken accessToken = new AccessToken(access_token, access_token_secret); Twitter twitter = new TwitterFactory(builder.build()).getInstance(accessToken); twitter4j.Status response = twitter.updateStatus(status); // Log.d("*** Update Status: ",response.getText()); } catch (TwitterException e) { Log.d("*** Twitter Update Error: ", e.getMessage()); } return null; } protected void onPostExecute(String file_url) { pDialog.dismiss(); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "Status tweeted successfully", Toast.LENGTH_SHORT).show(); txtUpdate.setText(""); } }); } } private void logoutFromTwitter() { Editor e = mSharedPreferences.edit(); e.remove(PREF_KEY_OAUTH_TOKEN); e.remove(PREF_KEY_OAUTH_SECRET); e.remove(PREF_KEY_TWITTER_LOGIN); e.commit(); btnLogoutTwitter.setVisibility(View.GONE); btnUpdateStatus.setVisibility(View.GONE); txtUpdate.setVisibility(View.GONE); lblUpdate.setVisibility(View.GONE); lblUserName.setText(""); lblUserName.setVisibility(View.GONE); btnLoginTwitter.setVisibility(View.VISIBLE); } private boolean isTwitterLoggedInAlready() { return mSharedPreferences.getBoolean(PREF_KEY_TWITTER_LOGIN, false); } protected void onResume() { super.onResume(); } }
More information on using Twitter4j can be found in the following resources:
- www.androidhive.info/2012/09/android-twitter-oauth-connect-tutorial/ by Ravi Tamada
- http://blog.doityourselfandroid.com/2011/08/08/improved-twitter-oauth-android/ by Do-it-yourself Android
- http://davidcrowley.me/?p=410 by David Crowley
- https://tutsplus.com/tutorials/?q=true&filter_topic=90 by Sue Smith
- http://blog.blundell-apps.com/sending-a-tweet/ by Blundell
Recipe: Integrating with Facebook
Facebook has changed rapidly in the last couple of years, and it remains one of the top social networking sites. One thing the Facebook team has done recently is to clean up their documentation to help developers. The official documentation can be found at https://developers.facebook.com/docs/getting-started/facebook-sdk-for-android/3.0/.
To get started with Facebook development, first download the Facebook SDK and the Facebook android package (APK) from https://developers.facebook.com/resources/facebook-android-sdk-3.0.zip. The APK is provided as a means of authentication without having to use a WebView. If the Facebook application is already installed on the phone, the APK file need not be installed.
Next, add the Facebook SDK as a library project to the Eclipse installation. This is done by choosing File → Import and then General → Existing Projects into Workspace. Note that Facebook warns against using the “Copy projects into workspace” options, as this may build incorrect filesystem paths and cause the SDK to function incorrectly.
After the Facebook SDK has been imported, the sample projects are available for experimentation. Note that most of the projects require the generation of a key hash that will be used to sign applications and that developers can add to their Facebook developer profile for quick SDK project access.
The key is generated by using the keytool utility that comes with Java. Open a terminal or command prompt and type the following to generate the key:
OS X:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | [ccc]openssl sha1 -binary | openssl base64
Windows:
keytool -exportcert -alias androiddebugkey -keystore %HOMEPATH%\.android\debug. keystore [ccc]| openssl sha1 -binary | openssl base64
The command should be typed in a single line, although terminals or command prompt windows may show it breaking into multiple lines. When the command is executed, a password prompt should appear. The password to enter is android. After the key has been generated successfully, it will be displayed. Note that if a “'keytool' is not recognized as an internal or external command . . .” error is generated, move to the bin directory of the JRE installation directory and try again. If there is a similar error for “openssl,” download OpenSSL from http://code.google.com/p/openssl-for-windows/. If there are still errors, make sure that the bin directories have been added to the system path or that the exact directories are being used instead of %HOMEPATH%.
If more than one computer will be used for development, a hash must be generated for each one and added to the developer profile at https://developers.facebook.com/.
Once that is done, dig into the sample applications and log in with them. The showcase example project, called HelloFacebookSample, demonstrates how to access a profile, update a status, and even upload photos.
The last step in creating an application that integrates with Facebook is to create a Facebook app that will then be tied to the Android application by using a generated key hash. This will take care of integration and allow users to authenticate themselves while using the application.
The developer site gives a terrific breakdown of all the pieces needed to get started. Be sure to read the official Scrumptious tutorial, which can be found at http://developers.facebook.com/docs/tutorials/androidsdk/3.0/scrumptious/.