Quantcast
Channel: Do it yourself Android » oauth
Viewing all articles
Browse latest Browse all 10

30 minute guide to integrating Foursquare in your Android application

$
0
0
In this article, I’m going to show you how you can use Foursquare in your Android application using OAuth 2.0.

We’ll be using 

I’ve decided to use the foursquare-api-java, a project hosted at Google Code, as it offers a rich interface to interact with Foursquare. The same API calls are possible through the Google API client for Java, but it would require you to write your own model classes to do the JSON to Java translation.
I will post some sample code here on how this can be done with the Google APIs client library for java.

In the sample application hosted at Github, we’re going to display a map where the user can select a location. Upon selecting a location, we’ll load up a list of Foursquare venues that the user can select.
Upon selecting a venue, we’ll return to our map, put a marker on the map representing the venue, and allow the user to perform a checkin.

The sample application

All dependencies are included in the lib folder of the project, so after doing a checkout of Github, you should have no problem

  • checking out everything from Git
  • importing the project in Eclipse and run it from there.
  • change your Oauth2 properties
  • run the project on your emulator or phone

This is by no means a complete production ready sample. The goal of the sample is to show you how to do the OAuth2 flow, retrieve an access token, and make some API calls.
The Oauth2 properties are defined in the OAuth2ClientCredentials class,
You’ll need to register a new OAuth2 consumer on the Foursquare website (Foursquare Application Registration), where you need to provide

  • an application name (ex: AndroidFSSample)
  • an application website (ex: http://localhost:8888)
  • a callback URL (ex: http://localhost:8888)

After registration, you should see something like this:

Our main Foursquare activity

Our main activity will allow the user to authorize against foursquare. It shows 2 buttons allowing to user to start the authorization process, and another button to clear the access tokens received from Foursquare.

/**
 * Launch the OAuth flow to get an access token required to do authorized API calls.
 * When the OAuth flow finishes, we redirect to this Activity to perform the API call.
 */
Button launchOauth = (Button) findViewById(R.id.btn_launch_oauth);
launchOauth.setOnClickListener(new View.OnClickListener() {
	public void onClick(View v) {
		startActivity(new Intent().setClass(v.getContext(),OAuthAccessTokenActivity.class));
	}
});

/**
 * Clearing the credentials and performing an API call to see the unauthorized message.
 */
Button clearCredentials = (Button) findViewById(R.id.btn_clear_credentials);
clearCredentials.setOnClickListener(new View.OnClickListener() {
	public void onClick(View v) {
		clearCredentials();
		new PerformApiCallTask().execute();
	}

});

The OAuth2 flow

The project will use the Google API Java client to do the Foursquare OAuth2.0 flow. We’ll start by defining the activity that will allows us to retrieve an OAuth 2.0 access token. It’s a simple activity that is dedicated for this task.

		<activity android:name=".OAuthAccessTokenActivity" 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="http" android:host="localhost"  />
			</intent-filter>
		</activity>

This activity doesn’t come with its own layout file, but rather embeds a WebView component programmatically. We’ll be using the WebView component to launch the Foursquare authorization URL, where we’ll need to login, and authorize the application access to our Foursquare data.
We enable javascript on the webview component, set it to visible (more on that later), and set it to the ContentView.

	@Override
	protected void onResume() {
		super.onResume();
		WebView webview = new WebView(this);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.setVisibility(View.VISIBLE);
        setContentView(webview);

You’ll notice that there a lot of code between the snippet above and the code that actually launches the authorization URL, but more on that later.
In order to retrieve an access token, we need to redirect the user to the Foursquare authorization URL (More info can be found on https://developer.foursquare.com/docs/oauth.html).
We build up an AuthorizationRequestUrl object (part of Google APIs client library for Java), pass on the Client ID and the Redirection URI (=callback) as defined when registering our consumer, and we launch the URL.

AuthorizationRequestUrl authorizationRequestUrl = new AuthorizationRequestUrl(OAuth2ClientCredentials.AUTHORIZATION_URL);
authorizationRequestUrl.clientId = OAuth2ClientCredentials.CLIENT_ID;
authorizationRequestUrl.redirectUri = OAuth2ClientCredentials.REDIRECT_URI;
webview.loadUrl(authorizationRequestUrl.build());

The snippet above will pop the following screen:
When pressing the login button, you can login with your Foursquare credentials


And after that, you should see the following screen (the fact that you now see a map instead of the error message means you’ve authenticated successfully to Foursquare. :


For those of you that looked at Oauth 2.0 flow in Android article, you might recall we used the following code to build up an authorization URL for the Latitude API

String authorizationUrl = new GoogleAuthorizationRequestUrl(OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.OAUTH_CALLBACK_URL, OAuth2ClientCredentials.SCOPE).build();

The Google APIs client library for Java offers ready made Google specific OAuth2 classes. Obviously, it doesn’t offer those for APIs such as Twitter,Foursquare,Gowalla or Facebook, but it does offer the base classes (AuthorizationRequestUrl) that you can use to construct the same authorization URL.

The code that’s sitting in between the 2 snippets is what will allow us to intercept the access token that is provided by Foursquare once you hit the authorize button in the embedded
browser. The OAuth2.0 flow used here is a web based flow that does the following:

  • redirects the user to Foursquare
  • allows the user to login using the Foursquare login page (meaning no password is captured by the browser)
  • allows foursquare to check your credentials and present you with an auhorization screen.
  • On this screen, you can allow this application access to your Foursquare data, or deny access.
  • If you decide to grant access, foursquare redirects to another page referred to as a callback URL, containing a token that allows this application to access your data.

We need to be able to intercept this redirect, capture the token, and store it on the phone so we can begin interacting with Foursquare.

Retrieving the access token is illustrated by the following code. Using the WebView component, we intercept the redirect, check if there is a token in the URL,
and if so, continue our processing.

@Override
public void onPageFinished(WebView view, String url)  {
	if (url.startsWith(OAuth2ClientCredentials.REDIRECT_URI)) {
	            		try {
	            			if (url.indexOf("code=")!=-1) {
		            			String code = extractCodeFromUrl(url);

Once we’ve retrieved the code from the Foursquare server, we can exchange it for an access token. This is done using the following code:

AuthorizationCodeGrant request = new AuthorizationCodeGrant(new NetHttpTransport(),
        new JacksonFactory(),
        OAuth2ClientCredentials.ACCESS_TOKEN_URL,
        OAuth2ClientCredentials.CLIENT_ID,
        OAuth2ClientCredentials.CLIENT_SECRET,
        code,
        OAuth2ClientCredentials.REDIRECT_URI);
AccessTokenResponse accessTokenResponse = request.execute();

CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs);
credentialStore.write(accessTokenResponse );

For those who looked at my Oauth 2.0 flow in Android article, you’ll notice that the only difference when dealing with non Google APIs is that we use the AuthorizationCodeGrant class instead of the Google specific GoogleAuthorizationCodeGrant. Other than that, the code is pretty much identical. I’ve put the Google specific code here for reference:

  AccessTokenResponse accessTokenResponse = new GoogleAuthorizationCodeGrant(new NetHttpTransport(),
			      new JacksonFactory(),
			      OAuth2ClientCredentials.CLIENT_ID,
			      OAuth2ClientCredentials.CLIENT_SECRET,
			      code,
			      OAuth2ClientCredentials.REDIRECT_URI).execute();

  CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs);
  credentialStore.write(accessTokenResponse);

We store the access token in a credential store (the shared preferences of our application).
Now that the app has an access token, we can launch our main activity and start interacting with Foursquare.

When our main activity is launched initially, and the user has properly authorized our app to use its Foursquare data using the flow above, the user will be presented with a map.
When the user clicks on the map, he’ll be presented with a list of places that we fetched from Foursquare.

The clickable map

For those of you who want more info on howto setup an Android project with the MapView component, I suggest you have a look at Using Google Maps in your Android Application.

We’re using the following overlay to allow the user to tap on the map :

private class HelloItemizedOverlay extends ItemizedOverlay<OverlayItem>
{

	/**
	 * Calling populate here to avoid a nullpointerexception.
	 *
	 * @param defaultMarker
	 */
	public HelloItemizedOverlay(Drawable defaultMarker) {
		  super(boundCenterBottom(defaultMarker));
		  populate();
	}

	public void addOverlay(OverlayItem overlay) {
	    mOverlays.add(overlay);
	    populate();
	}

	@Override
	protected OverlayItem createItem(int i) {
	  return mOverlays.get(i);
	}

	@Override
	public int size() {
	  return mOverlays.size();
	}

	/**
	 *
	 * Triggered when the user clicks on the map.
	 *
	 */
	@Override
	public boolean onTap(GeoPoint p, MapView mapView) {
		HelloItemizedOverlay itemizedoverlay = (HelloItemizedOverlay) mapView.getOverlays().get(0);
        OverlayItem overlayitem = new OverlayItem(p, "Location at " + "title","snippet");
        itemizedoverlay.addOverlay(overlayitem);
        mapView.invalidate();
		popFoursquareVenueList(p.getLatitudeE6()/1E6,p.getLongitudeE6()/1E6);
		return true;
	}

}

As you can see, when tapping on the map, we put a marker on the map through the HelloItemizedOverlay, and pop the FoursquareVenueList Activity.
We launch the venues list screen by passing on the coordinates of the map that the user clicked on.

private void popFoursquareVenueList(double lat,double lng) {
	Intent intent = new Intent(getApplicationContext(),FourSquarePlacesList.class);
	intent.putExtra(Constants.PLACE_LAT_FIELD, lat);
	intent.putExtra(Constants.PLACE_LNG_FIELD,lng);
	startActivity(intent);
}

The FoursquareVenuesList activaty shows the Foursquare venues in a list like this

The FoursquareVenueList

Our FoursquareVenueList Activity extends ListActivity, as we are going to show the venues in a ListView. The ListActivity provides us with the basic infrastructure
to setup the list, and handle the item selection in the list.

The layout looks like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#FFFFFF"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingTop="10px">

	<TextView
        android:id="@+id/intro"
        android:textColor="#000000"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Pick a place"/>

    <ListView
 	android:id="@id/android:list"
 	android:textColor="#000000"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
 		android:drawSelectorOnTop="true"/>

    <TextView
    	android:textColor="#000000"
        android:id="@id/android:empty"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="empty"/>

</LinearLayout>

We retrieve the latitude/longitude that was passed on from the previous screen.

	if (getIntent().getExtras() != null) {
		lat = getIntent().getExtras().getDouble(Constants.PLACE_LAT_FIELD);
		lng = getIntent().getExtras().getDouble(Constants.PLACE_LNG_FIELD);
	}

We’re using the following asynctask to fetch the places and populate the list.

private class PlacesListRefresher extends AsyncTask<Uri, Void, Void> {

	@Override
	protected Void doInBackground(Uri... params) {

		try {
			Log.i(Constants.TAG, "Retrieving places at " + lat + "," + lng);
			Result<VenuesSearchResult> venues = getFoursquareApi().venuesSearch(
					lat + "," + lng, null, null, null, null, null, null,
					null, null, null, null);
			CompactVenue[] compactVenues = venues.getResult().getVenues();
			Log.i(Constants.TAG, "found " + compactVenues.length
					+ " places");
			for (CompactVenue compactVenue : compactVenues) {
				veneusMap.add(compactVenue);
			}
		} catch (Exception ex) {
			Log.e(Constants.TAG, "Error retrieving venues", ex);
		}
		return null;
	}

	@Override
	protected void onPostExecute(Void result) {
		setListAdapter(new FoursquareTableAdapter(veneusMap));
	}

}

Accessing the foursquare API

As you can see, we fire up the Foursquare API and do a venuesSearch, by passing on the latitude and longitude we retrieved in the previous step.
We loop over the venues array and store it in a map. All of this is happening in a background thread. When the background thread finished, we setup our listAdapter in order to display the results in the ListView.

The getFoursquareApi() method is implemented like this:

public FoursquareApi getFoursquareApi() {
	if (this.foursquareApi==null) {
		this.prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
		CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs);

		AccessTokenResponse accessTokenResponse = credentialStore.read();
		this.foursquareApi = new FoursquareApi(OAuth2ClientCredentials.CLIENT_ID,
				OAuth2ClientCredentials.CLIENT_SECRET,
				OAuth2ClientCredentials.REDIRECT_URI,
				accessTokenResponse.accessToken, new DefaultIOHandler());
	}
	return this.foursquareApi;

}

It fetches the accessToken from our CredentialStore, and initialized the FoursquareApi object that we use to fetch the venues.

Using Google APIs client for Java for the Foursquare API calls

You may have noticed that for making the actual API calls, we’re not using the Google APIs client libraries for Java, but instead are relying on a third-party client library. That’s one model of working, and an appropriate one if a rich third party client library like Foursquare API for Java is available. It integrates very nicely with Google APIs client for Java as we pass on the access token we retrieved from Google APIs client for Java to the Foursquare API for Java. As Google APIs client for Java is really good at handling the OAuth2 stuff, and the Foursquare API for Java is really good at doing the Foursquare stuff, this is a model that works.

This does not mean that we can’t use the Google APIs client libraries for Java to access the Foursquare REST endpoints. In the sample project, I’ve created a method called performFoursquareApiCallUsingGoogleApiJavaClient that shows 2 ways of using the Google APIs client libraries for Java to access the Foursquare REST endpoints. Let’s go over them.

Fetching the response as a String, and transforming it into JSON

One way of interacting with the Foursquare endpoint is to interpret the response as a String (parseAsString()), and convert it into some standard JSON structures. That way, you can get a hold of the data in a generic way.

	public void performFoursquareApiCallUsingGoogleApiJavaClient() throws Exception {
		AccessTokenResponse accessTokenResponse = credentialStore.read();
		HttpTransport transport = new NetHttpTransport();
		GenericUrl genericUrl = new GenericUrl(FOURSQUARE_API_ENDPOINT);
		genericUrl.put("ll",lat + "," + lng);
		HttpRequest httpRequest = createApiRequestFactory(transport, accessTokenResponse.accessToken).buildGetRequest(
				genericUrl);
		HttpResponse httpResponse = httpRequest.execute();
		JSONObject object = new JSONObject(httpResponse.parseAsString());
		JSONObject fourSquareResponse = (JSONObject) object.get("response");
		JSONArray groups = (JSONArray) fourSquareResponse.get("groups");
		JSONObject group = (JSONObject)groups.get(0);
		JSONArray items = (JSONArray)group.get("items");
		Log.i(Constants.TAG, "Found venues " + items);

The example above shows you how to traverse the JSON structure using standard JSONArray and JSONObject objects.

Have Google APIs client library for Java transform the response into a java model.

Another approach is to create your own model classes, and let the Google APIs client for Java to the transformation of the JSON string into your Java model. The example below illustrates just that. Note how we don’t parse the response as a String, but rather parse the response into a FoursquareResponse.class.

		
		httpRequest = createApiRequestFactory(transport, accessTokenResponse.accessToken).buildGetRequest(
				genericUrl);
		JsonHttpParser parser = new JsonHttpParser();
	    parser.jsonFactory = new JacksonFactory();
	    httpRequest.addParser(parser);
		httpResponse = httpRequest.execute();
		FoursquareResponse foursquareResponse2 = httpResponse.parseAs(FoursquareResponse.class);
		Venue[] venues = foursquareResponse2.response.groups[0].items;
		Log.i(Constants.TAG, "Found venues " + venues);
		
	}

The FoursquareResponse.class looks like this


public class FoursquareResponse implements Serializable {
	private static final long serialVersionUID = -383365244692781213L;

	@Key
	public Response response;
}

The Response class like this:

public class Response  implements Serializable {
	private static final long serialVersionUID = -8744243204974447941L;

	@Key
	public Group[] groups;
}

You get the picture….. This way, you get to work with a much richer interface. Instead of working with low level generic JSON objects, you now have a rich java based data model that you can use in your app. You no longer need to know the internal JSON structure, you just need to read the java model to get the data you want.

I opted for the third party library because there was one available for Foursquare in Java. If no API was available, I would have gone for this approach.

We’ve created a FoursquareTableAdapter that acts as a data adapter for the ListView component.

class FoursquareTableAdapter extends ArrayAdapter<CompactVenue> {
	FoursquareTableAdapter(List<CompactVenue> list) {
		super(FoursquareVenueList.this, R.layout.places_list_row, list);
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (convertView == null) {
			convertView = getLayoutInflater().inflate(R.layout.places_list_row, parent, false);
			holder = new ViewHolder();
			holder.txtPlaceName = (TextView) convertView.findViewById(R.id.row_placename);
			holder.txtPlaceAddress = (TextView) convertView.findViewById(R.id.row_placeaddress);
			holder.layout = (RelativeLayout) convertView.findViewById(R.id.row_layout);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		CompactVenue venue = getVenueMapFromAdapter(position);

		try {
			holder.txtPlaceName.setText(venue.getName());
			if (venue.getLocation().getAddress() != null && venue.getLocation().getAddress().length() > 0) {
				holder.txtPlaceAddress.setText(venue.getLocation().getAddress());
			} else {
				holder.txtPlaceAddress.setText(R.string.no_address_info_found);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

		return (convertView);
	}
}

The FoursquareTableAdapter is an ArrayAdapter that needs to be inialized with a list of CompactVenue objects. The getView method is responsible for filling up each row in the ListView component.
We use the ViewHolder pattern to optimize performance and memory management. The ViewHolder pattern avoids calling findViewById() when it is not necessary.

We also setup a AdapterView.OnItemClickListener on the ListView that is triggered when the user selects an item form the list.
As you can see here, we retrieve a ference to the current selected venue, wrap up the data in an intent, and startup our previous activity again, only this time
passing on the additional data related to the venue.

getListView().setOnItemClickListener(
	new AdapterView.OnItemClickListener() {

		@Override
		public void onItemClick(AdapterView<?> parent, View view,
				int position, long id) {

			CompactVenue venue = veneusMap.get((int) id);
			Intent intent = new Intent(getApplicationContext(),FoursquareApiSample.class);
			intent.putExtra(Constants.PLACE_ID_FIELD, venue.getId());
			intent.putExtra(Constants.PLACE_LAT_FIELD, venue.getLocation().getLat());
			intent.putExtra(Constants.PLACE_LNG_FIELD, venue.getLocation().getLng());
			intent.putExtra(Constants.PLACE_NAME_FIELD, venue.getName());
			intent.putExtra(Constants.PLACE_ADDRESS_FIELD, venue.getLocation().getAddress());
			startActivity(intent);
		}

	}
);

The initial activity showing the map will pick up the venue data, put a marker on the map where the venue is located, and allow the user to checkin to that venu.

if (getIntent().getExtras()!=null) {
	Double lat = getIntent().getExtras().getDouble(Constants.PLACE_LAT_FIELD);
	Double lng = getIntent().getExtras().getDouble(Constants.PLACE_LNG_FIELD);
	venueAddress = getIntent().getExtras().getString(Constants.PLACE_ADDRESS_FIELD);
	venueName = getIntent().getExtras().getString(Constants.PLACE_NAME_FIELD);
	venueId = getIntent().getExtras().getString(Constants.PLACE_ID_FIELD);
	GeoPoint p = new GeoPoint((int) (lat * 1E6), (int) (lng * 1E6));
	HelloItemizedOverlay venueOverlay = (HelloItemizedOverlay) mapView.getOverlays().get(0);
    OverlayItem overlayitem = new OverlayItem(p, venueName, venueAddress);
    venueOverlay.addOverlay(overlayitem);
    mc.animateTo(p);
    mc.setCenter(p);
    mc.setZoom(16);
    showCheckinSection = true;
    this.checkinSection.setVisibility(View.VISIBLE);
}

You should see the following screen :

The actual checkin is done using again an AsyncTask, making sure that the actual checkin logic occurs in the background (as it involves performing a REST call to Foursquare,
potentially blocking the UI untill Foursquare comes back with a response).

private class CheckinTask extends AsyncTask<Uri, Void, Void> {

	private String apiStatusMsg;

	@Override
	protected Void doInBackground(Uri...params) {
		try {
			CredentialStore credentialStore = new SharedPreferencesCredentialStore(prefs);
			AccessTokenResponse accessTokenResponse = credentialStore.read();
		    FoursquareApi foursquareApi = new FoursquareApi(OAuth2ClientCredentials.CLIENT_ID, OAuth2ClientCredentials.CLIENT_SECRET, OAuth2ClientCredentials.REDIRECT_URI,accessTokenResponse.accessToken,new DefaultIOHandler());
			Result<Checkin> result = foursquareApi.checkinsAdd(venueId, null, getString(R.string.checkin_msg), null, null,null,null,null);
			if (result.getMeta().getCode()==200) {
				apiStatusMsg = "Checked in to " + venueName;
			} else {
				apiStatusMsg = result.getMeta().getErrorDetail();
			}
		} catch (FoursquareApiException e) {
			e.printStackTrace();
		}
        return null;
	}

	@Override
	protected void onPreExecute() {
	}

	@Override
	protected void onPostExecute(Void result) {
		Toast.makeText(FoursquareApiSample.this, apiStatusMsg, Toast.LENGTH_LONG).show();
	}

}

When the checkin succeeds, you should see a Toast message indicating that the checkin was successful.

You can double-check your Foursquare history to see that the checkin did in fact take place:

Conclusion

As I already showed in my Improved Twitter OAuth for Android post, the Google API client library for Java is well suited to interact with non Google APIs. Where the Twitter post showed you how to use the library with an OAuth 1.0a enabled service provider, this post shows that the Google API client library for Java can also be used to interact with non-Google APIs using Oauth 2.0. It’s strong collection of base OAuth2 objects make it very easy to interact with any OAuth2 enabled service provider. The library is available for a large number of languages (PHP,Python,Objective-C,..).
You can use parts of the API as you see fit. For example here I decided to use the OAuth 2.0 part, but not use the JSON to Java Model mapping as there was already a good Foursquare library available. For REST APIs support Oauth that don’t have any java based library available, it’s well worth the effort to model the API in Java, and let Google APIs Client Library for Java do the transformation for you.
I suggest you check it out at Google APIs Client Library for Java.

References


Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images