Martin Paul Eve bio photo

Martin Paul Eve

Professor of Literature, Technology and Publishing at Birkbeck, University of London

Email Books Twitter Google+ Github Stackoverflow MLA CORE Institutional Repo Hypothes.is ORCID ID   ORCID iD

Email Updates

In the last day before I head off for a bit of a Christmas break, I decided to take up a recent proposition to start work on an Android client for Mendeley. So far, in a couple of hours, I've got as far as implementing the OAuth provider, so you can start the application, log in to Mendeley and it will ask you to authorize the application to access your Mendeley account.

Mendroid screenshot

Well, that's the theory... there's a problem, though. Currently the OAuth system will not work with the Android browser. I took the same URL and request token, accessing the auth URL in one instance with a desktop client and one instance with an android phone (tried on device -- HTC Wildfire -- and emulator). The desktop PC can get an access code, the Android device cannot.

Apologies for the quality of that YouTube video, but it does demonstrate the point. As an additional debug procedure, I also ran the working desktop URL to the phone via Chrome to Phone. Same result.

Is Mendeley's API doing something strange based on user agent? Also: does Mendeley's API support a callback URL? (this is kinda crucial for an Android app!)

To try and get some feedback, here's the code it's running for login at present. This code is GPL v3 licensed and I have removed my own keys.

/*
    mendroid: An Android Mendeley client
    Copyright 2010 Martin Paul Eve

    This file is part of Mendroid.

    Mendroid is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    Mendroid is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with Mendroid.  If not, see <http://www.gnu.org/licenses/>.

*/
package com.martineve.mendroid;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Map;

import net.oauth.OAuth;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthServiceProvider;
import net.oauth.client.OAuthClient;
import net.oauth.client.httpclient4.HttpClient4;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MendeleyDroidLogin extends Activity implements OnClickListener {
	
	OAuthAccessor client;
	String access_token;
	String request_token;
	
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button login_button = (Button) findViewById(R.id.login_button);
        login_button.setOnClickListener(this);
        
        // setup the link in the credits box ;)
        TextView credits = (TextView) findViewById(R.id.login_page_credits);
        credits.setText(Html.fromHtml("Copyright <a href=\"http://www.martineve.com\">Martin Paul Eve</a>, 2011"));
        credits.setMovementMethod(LinkMovementMethod.getInstance());
    }
    
    protected void onResume() {
    	// extract the OAUTH access token if it exists
	    Uri uri = this.getIntent().getData();
	    if(uri != null) {
	    	ProgressDialog dialog = ProgressDialog.show(this, "", 
	                "Logging in to Mendeley, please wait...", true);
	      access_token = uri.getQueryParameter("oauth_token");
	      access_token = convertToAccessToken(client.requestToken);
	      // setDefaultAccessToken(access_token, this); // keep this in the database
	      dialog.cancel();
	    }
	    
	    super.onResume();
	}  
	    
    public String convertToAccessToken(String request_token) {
	    ArrayList<Map.Entry<String, String>> params = new ArrayList<Map.Entry<String, String>>();
	    OAuthClient oclient = new OAuthClient(new HttpClient4());
	    OAuthAccessor accessor = client;
	    params.add(new OAuth.Parameter("oauth_token", request_token));
	    try {
	    OAuthMessage omessage = oclient.invoke(accessor, "POST", accessor.consumer.serviceProvider.accessTokenURL, params);
	    return omessage.getParameter("oauth_token");
	    } catch (Exception ioe) {
	    	return "";
	    }
    }
    
    public void onClick(View v) {
    	// get the request token
    	ProgressDialog dialog = ProgressDialog.show(this, "", 
               "Communicating with Mendeley, please wait...", true);
    	client = defaultClient();
    	
    	dialog.cancel();
    	
    	Intent i = new Intent(Intent.ACTION_VIEW);
    	i.setData(Uri.parse(client.consumer.serviceProvider.userAuthorizationURL +
    	                                  "?oauth_token=" + client.requestToken +
    	                                  "&oauth_callback=" + client.consumer.callbackURL));
    	startActivity(i);
    	                             
    }
    
    OAuthServiceProvider defaultProvider() {
	    return new OAuthServiceProvider("http://www.mendeley.com/oauth/request_token/", "http://www.mendeley.com/oauth/authorize/", "http://www.mendeley.com/oauth/access_token/");
	}

	OAuthAccessor defaultClient() {
	    String callbackUrl = "martineve-mendroid:///";
	    OAuthServiceProvider provider =  defaultProvider();
	    
	    String consumerKey = "CONSUMER_KEY_HERE";
    	String consumerSecret = "CONSUMER_SECRET_HERE";
	    OAuthConsumer consumer = new OAuthConsumer(callbackUrl, consumerKey,
	                                    consumerSecret, provider);
	    OAuthAccessor accessor = new OAuthAccessor(consumer);
	    
    	OAuthClient oaclient = new OAuthClient(new HttpClient4());
    	
    	try {
			oaclient.getRequestToken(accessor);
			request_token = accessor.requestToken;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (OAuthException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    
	    return accessor;
	}

}

For now, I'm off on holiday, but if anyone had any thoughts, I'd love to hear them. Merry Christmas all!