Integrating Dropbox in Android Apps

To integrate dropbox in android app we have to create a online app on  dropbox.com which will be used by android app to communicate with dropbox.

Follow these steps to create online app

1. Open www.dropbox.com/developers/apps and login using your credentials

2. Click on create app to start the online app creation wizard.

3. Now click on Dropbox API app

4. Now select “Files and datastores” as we will be downloading files.

5. Next select “No” as we will be using uploaded pics which already exist on dropbox.

6. Next allow access to all type of files.

7. Now provide a unique name to your application and then check the agreement and hit “Create app”.

8. We are done with the setup and dropbox developer api will now show “App key” and “App secret”. copy these somewhere we will be needing these in android app we will create.

Creating Android app

1. Create a new project in Eclipse by navigating to File ⇒ New Android ⇒ Application Project and fill required details.

2. Go to www.dropbox.com/developers/core/sdks/android to download the SDK.

3. Extract this zip file and copy “dropbox-android-sdk-1.6.3.jar” and “json_simple-1.1.jar” from folder dropbox-android-sdk-1.6.3\lib and paste it to lib folder in android project.

 4. Next you have to Add “INTERNET” permission in AndroidManifest.xml. You also have to add entry for “com.dropbox.client2.android.AuthActivity” in manifest file.

Note : You have to replace “xxxxxxxxxxxxxxxx” in line number 33 with the app-key you got when you created app on dropbox.com.

5. Next we have to update activity_main.xml . We have to add couple buttons and ImageView to suit our app need. Copy the following code to it.

6. Next we have to add functionality to handle dropbox login and session maintenance in MainActivity.java.

You’ll need to provide your app key and secret as well as the permission you selected when creating the app. The permission will be represented by the AccessType enum (either APP_FOLDER or DROPBOX).

Pass all three values to the new DropboxAPI object.

Now we’re all set to start the authentication flow. We’ll start by calling the startOAuth2Authentication() method which will ask the user to authorize your app. If the Dropbox app is installed, the SDK will switch to it so the user doesn’t have to sign in, and it will fallback to the browser if not.

[java] // MyActivity below should be your activity class name
mDBApi.getSession().startOAuth2Authentication(MyActivity.this);
[/java]

Upon authentication, users are returned to the activity from which they came. To finish authentication after the user returns to your app, you’ll need to put the following code in your onResume function.[java] protected void onResume() {
super.onResume();

if (mDBApi.getSession().authenticationSuccessful()) {
try {
// Required to complete auth, sets the access token on the session
mDBApi.getSession().finishAuthentication();

String accessToken = mDBApi.getSession().getOAuth2AccessToken();
} catch (IllegalStateException e) {
Log.i(“DbAuthLog”, “Error authenticating”, e);
}
}
}

[/java]

The finishAuthentication() method will bind the user’s access token to the session. You’ll now be able to retrieve it via mDBApi.getSession().getOAuth2AccessToken().

You’ll need this token again after your app closes, so it’s important to save it for future access (though it’s not shown here). If you don’t, the user will have to re-authenticate every time they use your app. A common way to implement storing keys is through Android’s SharedPreferences API.

Final MainActivity.java will be like this :

Note: please add your app key and app secret in highlighted code.

[java highlight=”33,34″] package com.example.dropboxsample;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.android.AndroidAuthSession;
import com.dropbox.client2.session.AppKeyPair;

public class MainActivity extends Activity {
private static final String TAG = “MainActivity”;

private static final String APP_KEY = “YOUR APP KEY”;
private static final String APP_SECRET = “YOUR APP SERCRET”;

// You don’t need to change these, leave them alone.
private static final String ACCOUNT_PREFS_NAME = “prefs”;
private static final String ACCESS_KEY_NAME = “ACCESS_KEY”;
private static final String ACCESS_SECRET_NAME = “ACCESS_SECRET”;

DropboxAPI<AndroidAuthSession> mApi;

private boolean mLoggedIn;

// Android widgets
private Button mSubmit;
private LinearLayout mDisplay;
private Button mPhoto;
private Button mRoulette;

private ImageView mImage;

private final String PHOTO_DIR = “/Photos/”;

private static final int NEW_PICTURE = 1;
private String mCameraFileName;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (savedInstanceState != null) {
mCameraFileName = savedInstanceState.getString(“mCameraFileName”);
}

// We create a new AuthSession so that we can use the Dropbox API.
AndroidAuthSession session = buildSession();
mApi = new DropboxAPI<AndroidAuthSession>(session);

// Basic Android widgets
setContentView(R.layout.activity_main);

mSubmit = (Button) findViewById(R.id.auth_button);

mSubmit.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// This logs you out if you’re logged in, or vice versa
if (mLoggedIn) {
logOut();
} else {
// Start the remote authentication
mApi.getSession().startOAuth2Authentication(
MainActivity.this);
}
}
});

mDisplay = (LinearLayout) findViewById(R.id.logged_in_display);

// This is where a photo is displayed
mImage = (ImageView) findViewById(R.id.image_view);

// This is the button to take a photo
mPhoto = (Button) findViewById(R.id.photo_button);

mPhoto.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent();
// Picture from camera
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);

// This is not the right way to do this, but for some reason,
// having
// it store it in
// MediaStore.Images.Media.EXTERNAL_CONTENT_URI isn’t working
// right.

Date date = new Date();
DateFormat df = new SimpleDateFormat(“yyyy-MM-dd-kk-mm-ss”,
Locale.US);

String newPicFile = df.format(date) + “.jpg”;
String outPath = new File(Environment
.getExternalStorageDirectory(), newPicFile).getPath();
File outFile = new File(outPath);

mCameraFileName = outFile.toString();
Uri outuri = Uri.fromFile(outFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outuri);
Log.i(TAG, “Importing New Picture: ” + mCameraFileName);
try {
startActivityForResult(intent, NEW_PICTURE);
} catch (ActivityNotFoundException e) {
showToast(“There doesn’t seem to be a camera.”);
}
}
});

// This is the button to take a photo
mRoulette = (Button) findViewById(R.id.roulette_button);

mRoulette.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
DownloadPicture download = new DownloadPicture(
MainActivity.this, mApi, PHOTO_DIR, mImage);
download.execute();
}
});

// Display the proper UI state if logged in or not
setLoggedIn(mApi.getSession().isLinked());

}

@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString(“mCameraFileName”, mCameraFileName);
super.onSaveInstanceState(outState);
}

@Override
protected void onResume() {
super.onResume();
AndroidAuthSession session = mApi.getSession();

// The next part must be inserted in the onResume() method of the
// activity from which session.startAuthentication() was called, so
// that Dropbox authentication completes properly.
if (session.authenticationSuccessful()) {
try {
// Mandatory call to complete the auth
session.finishAuthentication();

// Store it locally in our app for later use
storeAuth(session);
setLoggedIn(true);
} catch (IllegalStateException e) {
showToast(“Couldn’t authenticate with Dropbox:”
+ e.getLocalizedMessage());
Log.i(TAG, “Error authenticating”, e);
}
}
}

// This is what gets called on finishing a media piece to import
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == NEW_PICTURE) {
// return from file upload
if (resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (data != null) {
uri = data.getData();
}
if (uri == null && mCameraFileName != null) {
uri = Uri.fromFile(new File(mCameraFileName));
}
File file = new File(mCameraFileName);

if (uri != null) {
UploadPicture upload = new UploadPicture(this, mApi,
PHOTO_DIR, file);
upload.execute();
}
} else {
Log.w(TAG, “Unknown Activity Result from mediaImport: “
+ resultCode);
}
}
}

private void logOut() {
// Remove credentials from the session
mApi.getSession().unlink();

// Clear our stored keys
clearKeys();
// Change UI state to display logged out version
setLoggedIn(false);
}

/**
* Convenience function to change UI state based on being logged in
*/
private void setLoggedIn(boolean loggedIn) {
mLoggedIn = loggedIn;
if (loggedIn) {
mSubmit.setText(“Unlink from Dropbox”);
mDisplay.setVisibility(View.VISIBLE);
} else {
mSubmit.setText(“Link with Dropbox”);
mDisplay.setVisibility(View.GONE);
mImage.setImageDrawable(null);
}
}

private void showToast(String msg) {
Toast error = Toast.makeText(this, msg, Toast.LENGTH_LONG);
error.show();
}

/**
* Shows keeping the access keys returned from Trusted Authenticator in a
* local store, rather than storing user name & password, and
* re-authenticating each time (which is not to be done, ever).
*/
private void loadAuth(AndroidAuthSession session) {
SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
String key = prefs.getString(ACCESS_KEY_NAME, null);
String secret = prefs.getString(ACCESS_SECRET_NAME, null);
if (key == null || secret == null || key.length() == 0
|| secret.length() == 0)
return;

if (key.equals(“oauth2:”)) {
// If the key is set to “oauth2:”, then we can assume the token is
// for OAuth 2.
session.setOAuth2AccessToken(secret);
}
}

/**
* Shows keeping the access keys returned from Trusted Authenticator in a
* local store, rather than storing user name & password, and
* re-authenticating each time (which is not to be done, ever).
*/
private void storeAuth(AndroidAuthSession session) {
// Store the OAuth 2 access token, if there is one.
String oauth2AccessToken = session.getOAuth2AccessToken();
if (oauth2AccessToken != null) {
SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME,
0);
Editor edit = prefs.edit();
edit.putString(ACCESS_KEY_NAME, “oauth2:”);
edit.putString(ACCESS_SECRET_NAME, oauth2AccessToken);
edit.commit();
return;
}
}

private void clearKeys() {
SharedPreferences prefs = getSharedPreferences(ACCOUNT_PREFS_NAME, 0);
Editor edit = prefs.edit();
edit.clear();
edit.commit();
}

private AndroidAuthSession buildSession() {
AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, APP_SECRET);

AndroidAuthSession session = new AndroidAuthSession(appKeyPair);
loadAuth(session);
return session;
}
}
[/java]

7. Handling file Uploading

Let’s say we’re building a text editing app and we want to use it to save your latest magnum opus to Dropbox. Let’s browse the methods in the Javadoc to see which one will do that for us. This page lists all the methods supported in the SDK. If you scroll down, you’ll find putFile.

Like most methods in the Core API, putFile makes a network call, so make sure to invoke it on a background thread. (If you run this on the main thread, you’ll see a NetworkOnMainThreadException.)

putFile takes a path pointing to where we want the file on our Dropbox, an InputStream to be uploaded there, and the input’s length. For this example, let’s upload a local copy of working-draft.txt:

[java] File file = new File(“working-draft.txt”);
FileInputStream inputStream = new FileInputStream(file);
Entry response = mDBApi.putFile(“/magnum-opus.txt”, inputStream,
file.length(), null, null);
Log.i(“DbExampleLog”, “The uploaded file’s rev is: ” + response.rev);
[/java]

If all goes well, the data in your local working-draft.txt will now be in the root of your app folder (or Dropbox folder, depending on your app’s access type). The variable response will be an Entry object containing the metadata of the newly uploaded file.

Now create file named “UploadPicture.java” to handle this functionality. The code will go like this.

[java]

package com.example.dropboxsample;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
import android.widget.Toast;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.DropboxAPI.UploadRequest;
import com.dropbox.client2.ProgressListener;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.exception.DropboxFileSizeException;
import com.dropbox.client2.exception.DropboxIOException;
import com.dropbox.client2.exception.DropboxParseException;
import com.dropbox.client2.exception.DropboxPartialFileException;
import com.dropbox.client2.exception.DropboxServerException;
import com.dropbox.client2.exception.DropboxUnlinkedException;

/**
* Here we show uploading a file in a background thread, trying to show typical
* exception handling and flow of control for an app that uploads a file from
* Dropbox.
*/
public class UploadPicture extends AsyncTask<Void, Long, Boolean> {

private DropboxAPI<?> mApi;
private String mPath;
private File mFile;

private long mFileLen;
private UploadRequest mRequest;
private Context mContext;
private final ProgressDialog mDialog;

private String mErrorMsg;

public UploadPicture(Context context, DropboxAPI<?> api,
String dropboxPath, File file) {
// We set the context this way so we don’t accidentally leak activities
mContext = context.getApplicationContext();

mFileLen = file.length();
mApi = api;
mPath = dropboxPath;
mFile = file;

mDialog = new ProgressDialog(context);
mDialog.setMax(100);
mDialog.setMessage(“Uploading ” + file.getName());
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setProgress(0);
mDialog.setButton(ProgressDialog.BUTTON_POSITIVE, “Cancel”,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// This will cancel the putFile operation
mRequest.abort();
}
});
mDialog.show();
}

@Override
protected Boolean doInBackground(Void… params) {
try {
// By creating a request, we get a handle to the putFile operation,
// so we can cancel it later if we want to
FileInputStream fis = new FileInputStream(mFile);
String path = mPath + mFile.getName();
mRequest = mApi.putFileOverwriteRequest(path, fis, mFile.length(),
new ProgressListener() {
@Override
public long progressInterval() {
// Update the progress bar every half-second or so
return 500;
}

@Override
public void onProgress(long bytes, long total) {
publishProgress(bytes);
}
});

if (mRequest != null) {
mRequest.upload();
return true;
}

} catch (DropboxUnlinkedException e) {
// This session wasn’t authenticated properly or user unlinked
mErrorMsg = “This app wasn’t authenticated properly.”;
} catch (DropboxFileSizeException e) {
// File size too big to upload via the API
mErrorMsg = “This file is too big to upload”;
} catch (DropboxPartialFileException e) {
// We canceled the operation
mErrorMsg = “Upload canceled”;
} catch (DropboxServerException e) {
// Server-side exception. These are examples of what could happen,
// but we don’t do anything special with them here.
if (e.error == DropboxServerException._401_UNAUTHORIZED) {
// Unauthorized, so we should unlink them. You may want to
// automatically log the user out in this case.
} else if (e.error == DropboxServerException._403_FORBIDDEN) {
// Not allowed to access this
} else if (e.error == DropboxServerException._404_NOT_FOUND) {
// path not found (or if it was the thumbnail, can’t be
// thumbnailed)
} else if (e.error == DropboxServerException._507_INSUFFICIENT_STORAGE) {
// user is over quota
} else {
// Something else
}
// This gets the Dropbox error, translated into the user’s language
mErrorMsg = e.body.userError;
if (mErrorMsg == null) {
mErrorMsg = e.body.error;
}
} catch (DropboxIOException e) {
// Happens all the time, probably want to retry automatically.
mErrorMsg = “Network error. Try again.”;
} catch (DropboxParseException e) {
// Probably due to Dropbox server restarting, should retry
mErrorMsg = “Dropbox error. Try again.”;
} catch (DropboxException e) {
// Unknown error
mErrorMsg = “Unknown error. Try again.”;
} catch (FileNotFoundException e) {
}
return false;
}

@Override
protected void onProgressUpdate(Long… progress) {
int percent = (int) (100.0 * (double) progress[0] / mFileLen + 0.5);
mDialog.setProgress(percent);
}

@Override
protected void onPostExecute(Boolean result) {
mDialog.dismiss();
if (result) {
showToast(“Image successfully uploaded”);
} else {
showToast(mErrorMsg);
}
}

private void showToast(String msg) {
Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
error.show();
}
}
[/java]

8. Handling file download.

Some time has passed and you’re ready to start editing that magnum opus of yours again. We’ll need the getFile method to download the file.

[java] File file = new File(“/magnum-opus.txt”);
FileOutputStream outputStream = new FileOutputStream(file);
DropboxFileInfo info = mDBApi.getFile(“/magnum-opus.txt”, null, outputStream, null);
Log.i(“DbExampleLog”, “The file’s rev is: ” + info.getMetadata().rev);
[/java]

Just like the putFile method, we’re using a dummy output stream to handle the file contents. In addition to the file, getFile will return DropboxFileInfo, including an entry for metadata.

Create file named “DownloadPicture.java” to handle this functionality. The code will go like this.

[java] package com.example.dropboxsample;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.widget.ImageView;
import android.widget.Toast;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.DropboxAPI.Entry;
import com.dropbox.client2.DropboxAPI.ThumbFormat;
import com.dropbox.client2.DropboxAPI.ThumbSize;
import com.dropbox.client2.exception.DropboxException;
import com.dropbox.client2.exception.DropboxIOException;
import com.dropbox.client2.exception.DropboxParseException;
import com.dropbox.client2.exception.DropboxPartialFileException;
import com.dropbox.client2.exception.DropboxServerException;
import com.dropbox.client2.exception.DropboxUnlinkedException;

/**
* Here we show getting metadata for a directory and downloading a file in a
* background thread, trying to show typical exception handling and flow of
* control for an app that downloads a file from Dropbox.
*/

public class DownloadPicture extends AsyncTask<Void, Long, Boolean> {

private Context mContext;
private final ProgressDialog mDialog;
private DropboxAPI<?> mApi;
private String mPath;
private ImageView mView;
private Drawable mDrawable;

private FileOutputStream mFos;

private boolean mCanceled;
private Long mFileLen;
private String mErrorMsg;

// Note that, since we use a single file name here for simplicity, you
// won’t be able to use this code for two simultaneous downloads.
private final static String IMAGE_FILE_NAME = “dbroulette.png”;

public DownloadPicture(Context context, DropboxAPI<?> api,
String dropboxPath, ImageView view) {
// We set the context this way so we don’t accidentally leak activities
mContext = context.getApplicationContext();

mApi = api;
mPath = dropboxPath;
mView = view;

mDialog = new ProgressDialog(context);
mDialog.setMessage(“Downloading Image”);
mDialog.setButton(ProgressDialog.BUTTON_POSITIVE, “Cancel”,
new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mCanceled = true;
mErrorMsg = “Canceled”;

// This will cancel the getThumbnail operation by
// closing
// its stream
if (mFos != null) {
try {
mFos.close();
} catch (IOException e) {
}
}
}
});

mDialog.show();
}

@Override
protected Boolean doInBackground(Void… params) {
try {
if (mCanceled) {
return false;
}

// Get the metadata for a directory
Entry dirent = mApi.metadata(mPath, 1000, null, true, null);

if (!dirent.isDir || dirent.contents == null) {
// It’s not a directory, or there’s nothing in it
mErrorMsg = “File or empty directory”;
return false;
}

// Make a list of everything in it that we can get a thumbnail for
ArrayList<Entry> thumbs = new ArrayList<Entry>();
for (Entry ent : dirent.contents) {
if (ent.thumbExists) {
// Add it to the list of thumbs we can choose from
thumbs.add(ent);
}
}

if (mCanceled) {
return false;
}

if (thumbs.size() == 0) {
// No thumbs in that directory
mErrorMsg = “No pictures in that directory”;
return false;
}

// Now pick a random one
int index = (int) (Math.random() * thumbs.size());
Entry ent = thumbs.get(index);
String path = ent.path;
mFileLen = ent.bytes;

String cachePath = mContext.getCacheDir().getAbsolutePath() + “/”
+ IMAGE_FILE_NAME;
try {
mFos = new FileOutputStream(cachePath);
} catch (FileNotFoundException e) {
mErrorMsg = “Couldn’t create a local file to store the image”;
return false;
}

// This downloads a smaller, thumbnail version of the file. The
// API to download the actual file is roughly the same.
mApi.getThumbnail(path, mFos, ThumbSize.BESTFIT_960x640,
ThumbFormat.JPEG, null);
if (mCanceled) {
return false;
}

mDrawable = Drawable.createFromPath(cachePath);
// We must have a legitimate picture
return true;

} catch (DropboxUnlinkedException e) {
// The AuthSession wasn’t properly authenticated or user unlinked.
} catch (DropboxPartialFileException e) {
// We canceled the operation
mErrorMsg = “Download canceled”;
} catch (DropboxServerException e) {
// Server-side exception. These are examples of what could happen,
// but we don’t do anything special with them here.
if (e.error == DropboxServerException._304_NOT_MODIFIED) {
// won’t happen since we don’t pass in revision with metadata
} else if (e.error == DropboxServerException._401_UNAUTHORIZED) {
// Unauthorized, so we should unlink them. You may want to
// automatically log the user out in this case.
} else if (e.error == DropboxServerException._403_FORBIDDEN) {
// Not allowed to access this
} else if (e.error == DropboxServerException._404_NOT_FOUND) {
// path not found (or if it was the thumbnail, can’t be
// thumbnailed)
} else if (e.error == DropboxServerException._406_NOT_ACCEPTABLE) {
// too many entries to return
} else if (e.error == DropboxServerException._415_UNSUPPORTED_MEDIA) {
// can’t be thumbnailed
} else if (e.error == DropboxServerException._507_INSUFFICIENT_STORAGE) {
// user is over quota
} else {
// Something else
}
// This gets the Dropbox error, translated into the user’s language
mErrorMsg = e.body.userError;
if (mErrorMsg == null) {
mErrorMsg = e.body.error;
}
} catch (DropboxIOException e) {
// Happens all the time, probably want to retry automatically.
mErrorMsg = “Network error. Try again.”;
} catch (DropboxParseException e) {
// Probably due to Dropbox server restarting, should retry
mErrorMsg = “Dropbox error. Try again.”;
} catch (DropboxException e) {
// Unknown error
mErrorMsg = “Unknown error. Try again.”;
}
return false;
}

@Override
protected void onProgressUpdate(Long… progress) {
int percent = (int) (100.0 * (double) progress[0] / mFileLen + 0.5);
mDialog.setProgress(percent);
}

@Override
protected void onPostExecute(Boolean result) {
mDialog.dismiss();
if (result) {
// Set the image now that we have it
mView.setImageDrawable(mDrawable);
} else {
// Couldn’t download it, so show an error
showToast(mErrorMsg);
}
}

private void showToast(String msg) {
Toast error = Toast.makeText(mContext, msg, Toast.LENGTH_LONG);
error.show();
}

}
[/java]

 9. phew !! all done now. just build your project and then run on device.

Resources :

https://www.dropbox.com/developers/core/start/android

If you have any queries then please comment on the post.

Thank you.

guru

Technology enthusiast. Loves to tinker with things. Always trying to create something wonderful using technology. Loves coding for Android, Raspberry pi, Arduino , Opencv and much more.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *