Friday, September 28, 2012

Android RESTful/OAuth upload file to Dropbox

,

Introduction 

A few of my applications generate files locally and although  methods to transfer those files like Bluetooth/Wifi transfer , email and similar are included users are heavily using the Cloud so the option to write the files to the Cloud was due pronto. We can accomplish this  using the RESTful API/services from the Cloud provider.
 RESTFul services are becoming a predominant Web service  nowadays due to its simpler style specially its stateless mode. Clients on different platforms can access resources via this architecture with very low complexity. Clients need to authenticate to the server and  the OAuth is the  method of choice since it is an open and straightforward standard with a decent level of security. In this article we will walk through an Android implementation of uploading a file to dropbox using their RESTFul API which utilizes OAuth authentication.
Download source code - 150.2 K

Background

Most of the big players like Google, Facebook, Tweeter, etc. offer RESTful services with OAuth authentication.
The OAuth carries out the authentication with these few steps:
    
  • Ask the server for permission to request the user to access his/her account. To carry out this the client (e.g. our code) needs a pair of key/secret_word credentials which is available from the provider upon request.
  • Upon success we then ask the user for permission this usually requires for him/her to log in to the Dropbox account.
  • Upon success  a new set of key/secret_word credentials is provided and with this set handy all of the available APIs/methods can be accessed using this pair to sign each request, in our case we can upload files, read user info , etc.  
These RESTful APIs are nothing but URLs which are sent via a GET or POST http request. The OAUth authentication  requires a call to these URLs with a set of predefined args that include timestamps the key/secret_word set and the encryption type(SHA1, etc), this time stamp prevents from somebody else grabbing the URL and trying to call  it again since the time stamp will be different at a later time providing a good layer of security.
The syntax is straightforward:
<URL from the RESTful API>?<OAuth args ...>&<Dropbox args...>
For instance to start the Dropbox OAuth we need to call the URL below without any DropBpx args just the regular OAuth
https://api.dropbox.com/1/oauth/request_token?<OAuth args ..>

oauth_consumer_key=<key> provided by Dropbox
oauth_token=<secret_word> provided by Dropbox
oauth_nonce=<a number used once> a random string that is meant to uniquely identify each signed request.
oauth_timestamp=<when the request is sent>
oauth_signature_method=HMAC-SHA1 (it could be any other standard encryption used to generate the nonce)
oauth_version
The combination of nonce and timestamp takes the load off the server and allows  the Service Provider to only keep nonce values for a limited time. The particular signature specification can be acquired and implemented internally in our code but there are quite a few pre-cooked libraries already so no need to reinvent the wheel here we will be using the Signpost library to help us with the OAuth authentication steps.

Finally RESTful  providers like Dropbox also offer SDKs specific to the platforms (e.g. items for Android, iPhone, etc) but  these are nothing but wrappers of the basic RESTful APIs implemented for the specific platform.

1. Implementing the code 

The audience here is supposed to be well versed in Java/Android already so we will skip  the basics and get to the meat of the topic. Our basic screen looks like this.

1.1 Prerequisites: Manifest and Jar libraries

The Manifest needs to include a section that allows our App access to the net. Also our application needs an intent-filter so that our browser fires up properly and a definition of our dummy schema/URL used when we come back to our application from the browser.
<activity android:name=".main"
           ....
                  
            <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="myrest" android:host="myStep.com"  />
            </intent-filter>   
</activity>
...
<uses-permission android:name="android.permission.INTERNET">        
</uses-permission> 
 
We need to download the Signpost jar library and add it to our project ,we just drop it to the libs folder and add it 
Also we need to keep a reference to the Dropbox RESTful APIs.

1.2 Getting the credentials to sign an API request

Call the server properly to start up the process, the calls to the SingPost methods are highlighted
private static CommonsHttpOAuthConsumer consumer;
private static CommonsHttpOAuthProvider provider ;

...
// need to request this
String DropB_key="<request this From DropBox>";
String DropB_secret="<request this From DropBox>";
    
// from dropB API
String  request_url = "https://api.dropbox.com/1/oauth/request_token";
String  authorization_url = "https://www.dropbox.com/1/oauth/authorize";
String  access_url ="https://api.dropbox.com/1/oauth/access_token";    
    // to come back to our App after user authorizes
String  callBack_url ="myRest://myStep.com";   
    

...
// ask authorization to request the user
consumer = new CommonsHttpOAuthConsumer(
     DropB_key, DropB_secret);

provider = new CommonsHttpOAuthProvider(
    request_url, access_url, authorization_url); 

// we r good not let's ask the user's permission
try
{
  String authURL = provider.retrieveRequestToken(
            consumer, callBack_url);
        
  Intent intent2 = new Intent(Intent.ACTION_VIEW);

  intent2.setData( Uri.parse(authURL) );
  startActivity(intent2);
                      
}catch (Exception e)
{}
As you can see the highlighted Signpost library methods make it very straightforward to start out the authentication process taking care of all of the gory details. When we call the user for authorization we will fire a browser to get back to our App the callBack_url is set to a dummy schema and URL that we can catch  on the onResume event and wrap up the process.
..    @Override
public void onResume() 
{
    // upon confirmation the call back will come to here.
    super.onResume();

    Uri uri = this.getIntent().getData();
        
    ...
        
                
    // so it is coming back from the auth, catch it when it comes to our dummy URL
    if( uri != null)
    {
           ...    
        
        // now let's do the real stuff
        if( uri.getHost().equals("myStep.com"))
        {
            //grab the tokens
            String parms = uri.getEncodedQuery();
                
            // perfomr the write thru
            try {
    
                // grab the token/secret items to carry on
                String verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
                
                provider.retrieveAccessToken(consumer, verifier);
                String ACCESS_KEY = consumer.getToken();
                String ACCESS_SECRET = consumer.getTokenSecret();

                Log.d("OAuth Dropbox", ACCESS_KEY);
                Log.d("OAuth Dropbox", ACCESS_SECRET);
            
                ...
We have got the credentials to sign requests to the available RESTful APIs, we are good to go now.

1.3 Request user info and upload a file to Dropbox 

With the credentials acquired the rest is straightforward, we get the proper API (a URL from Dropbox) and carry out either a GET or a POST as specified by the API, Dropbox returns either a few values formatted as URL arguments, or a JSON set.
Each request must be signed with those credentials
We ask Dropbox for current user info below:
...
consumer.setTokenWithSecret(ACCESS_KEY, ACCESS_SECRET);

String uRL_file_list_req="https://api.dropbox.com/1/account/info";
HttpClient httpclient = new DefaultHttpClient();  
HttpGet request = new HttpGet(uRL_file_list_req );  

consumer.sign(request);


ResponseHandler<String> handler = new BasicResponseHandler();  
try {  
    result = httpclient.execute(request, handler);  
} catch (ClientProtocolException e) {  
    e.printStackTrace();  
    Toast.makeText(getApplicationContext(), "Protocol failure uploading the file",
            Toast.LENGTH_SHORT).show();
    return;
} catch (IOException e) {  
    e.printStackTrace();
    Log.e("******",Log.getStackTraceString(e)); 
    Toast.makeText(getApplicationContext(), "IO failure uploading the file",
            Toast.LENGTH_SHORT).show();
    return;
}  


JSONObject json_data = new JSONObject(result);
JSONArray nameArray = json_data.names();
JSONArray valArray = json_data.toJSONArray(nameArray);

...
We upload a text file with contents of the simple Android UI above, changing the proper MIME type and encoding the contents properly is all it is needed to upload any other file type like an image.
//
// last but not least let's upload a file
// default parm is overwritten so we keep it simple
String textStr = myContents.getText() + "";  
String fileName = fName.getText() + "";  


uRL_file_list_req="https://api-content.dropbox.com/1/files_put/"  + 
         "sandbox/" + fileName ; //+ "?param=val" ;

consumer.setTokenWithSecret(ACCESS_KEY, ACCESS_SECRET);


HttpPost request3 = new HttpPost(uRL_file_list_req );  


request3.addHeader("Content-Type", "text/plain"); 
request3.setEntity(new StringEntity(textStr)); 

...

consumer.sign(request3);


result="";
try {  
    result = httpclient.execute(request3, handler);  
} catch (ClientProtocolException e) {  
    e.printStackTrace();  
    Log.e("$$$$$$$",Log.getStackTraceString(e));
    Toast.makeText(getApplicationContext(), 
            "Protocol failure uploading the file",
            Toast.LENGTH_SHORT).show();
    return;
    
} catch (IOException e) 
{  
    e.printStackTrace();
    Log.e("******",Log.getStackTraceString(e)); 
    Toast.makeText(getApplicationContext(), 
            "IO failure uploading the file",
            Toast.LENGTH_SHORT).show();
    return;
}  

// parse out the data
json_data = new JSONObject(result);
nameArray = json_data.names();
valArray = json_data.toJSONArray(nameArray);

Toast.makeText(getApplicationContext(), "Succes!! - " + 
        valArray.getString(7) + " " + valArray.getString(10),
        Toast.LENGTH_SHORT).show();

httpclient.getConnectionManager().shutdown(); 
...

2. Caveats  

The only issue here  is that the  Dropbox documentation reference  is a bit blurry,  when you  need to carry file transfer operations the URL is of the form
https://api-content.dropbox.com/1/files/<root>/<path>
<root> is a string with a value of either dropbox or sandbox , when you request the initial credential pair from Dropbox it defaults you in Folder mode with root as sandbox. Later on you can request to change the status to production so users can use your App. It will create a folder under Apps with the name of your application.
<path> is the path to the actual file and it is relative to the App folder, e.g., to stat file test.txt sitting in the Apps/myCoolApp/ folder the above should be
https://api-content.dropbox.com/1/files/sandbox/text.txt

3. Final Thoughts  

Very straightforward as you can see just a matter of getting through the specification of the Dropbox API. Having the credential keys handy and stored as a local preference in your app can allow further queries or file operations to Dropbox from your application without any other user interaction with Dropbox.
The included sample code has the little extra details to get this working on Android since we are calling the RESTful API and not the SDK it is easy to port over to other platforms. Obviously the sample code is just a bare sample to get things going.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

becker666
Software Developer (Senior) BSC Inc
United States United States
Member

0 comments to “Android RESTful/OAuth upload file to Dropbox”

Post a Comment

 

Android Development Tutorials Copyright © 2011 -- Template created by O Pregador -- Powered by Blogger Templates