Using the Sync API on Android

The Sync API takes care of syncing all your data with Dropbox through a familiar, file system-like interface. It's like giving your app its own, private Dropbox client.

Here are the four things you'll need to work with when building on the Sync API:

Account manager

The account manager (DbxAccountManager) is your starting point. It lets your app start the authentication process to link with a user's Dropbox account. Once the app is linked, you can create a file system object.

File system

The file system object (DbxFileSystem) acts like a private, local file system that is automatically synced to Dropbox. You can perform all the standard operations you'd expect, such as listing files and folders, and editing, moving, or deleting them. Once the file system is created, it immediately starts syncing the file info (though not files' contents) for everything your app can see based on its permission.

Files

Once you've created or opened a file (DbxFile), you can start working with data. The Sync API will automatically download the file so you can read it. Write to the file and the Sync API will push any changes back to the server.

Listeners

You can also listen for changes to a file or path so you can update your app's state when new data is available. When a listener is called, check the file status to see what's changing.

If you want to follow along, set up a Sync API project before you dive in.

Heads up: The Sync API doesn't support the Full Dropbox permission. Learn more about the available permissions for API apps.

Linking accounts

To start interacting with the Sync API, you'll need to create a DbxAccountManager object. This object lets you link to a Dropbox user's account which is the first step to working with data on their behalf. Each Android Activity or Fragment that interacts with Dropbox can obtain a DbxAccountManager. A good place to do so is in your onCreate() method.

Be sure to replace APP_KEY and APP_SECRET with the real values for your app.
private DbxAccountManager mDbxAcctMgr;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mDbxAcctMgr = DbxAccountManager.getInstance(getApplicationContext(), APP_KEY, APP_SECRET);
}

The next step is to link to a user's account. Starting the linking process will launch an external Activity so your app will need to override onActivityResult() which will be called when linking is complete. You can specify any request code you want, it's just used to indicate the reason that onActivityResult() is being called (in this case, because the Dropbox account linking activity completed).

static final int REQUEST_LINK_TO_DBX = 0;  // This value is up to you

public void onClickLinkToDropbox(View view) {
    mDbxAcctMgr.startLink((Activity)this, REQUEST_LINK_TO_DBX);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_LINK_TO_DBX) {
        if (resultCode == Activity.RESULT_OK) {
            // ... Start using Dropbox files.
        } else {
            // ... Link failed or was cancelled by the user.
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

When onActivityResult() is called with the REQUEST_LINK_TO_DBX request code and RESULT_OK, you should see an info-level message in LogCat saying "Dropbox user <userid> linked." At this point, the account has been linked with the Account Manager.

The user only has to link once. Once they've linked, the Sync API will automatically store the user's account info on the device. When your app restarts, you can check whether it is already linked by calling hasLinkedAccount().

Working with files

Once you've linked to a user's account, you can get the associated DbxFileSystem which you'll use to access the user's Dropbox.

DbxFileSystem dbxFs = DbxFileSystem.forAccount(mDbxAcctMgr.getLinkedAccount());

With a file system in hand, you can start reading and writing files (represented by a DbxFile). If you're using the App folder permission, your App Folder won't contain any files when a user first links with your app, but it's easy to create one.

DbxFile testFile = dbxFs.create(new DbxPath("hello.txt"));
try {
    testFile.writeString("Hello Dropbox!");
} finally {
    testFile.close();
}

Writing to the file will succeed even if you are offline, and automatically sync to the server once your app comes back online. There are other ways to write to a file too (using a FileOutputStream or an existing file), depending on your app's needs. Reading a file is just as easy.

DbxFile testFile = dbxFs.open(testPath);
try {
    String contents = testFile.readString();
    Log.d("Dropbox Test", "File contents: " + contents);
} finally {
    testFile.close();
}

If the file isn't cached yet, readString() will wait while the file downloads. You can hide this wait from the UI using a Loader, or use a DbxFile.Listener to track the file's download progress. We'll tackle listeners next.

Listening for changes

A DbxFile is associated with a specific version of that file. You can check whether the file isCached and can be read immediately by using the DbxFile.getSyncStatus() method. If it's not cached, the readString() call will wait while it downloads. If you don't want to wait, you can register a listener to track its download progress.

DbxFileStatus status = testFile.getSyncStatus();
if (!status.isCached) {
    testFile.addListener(new DbxFile.Listener() {
        @Override
        public void onFileChange(DbxFile file) {
            // Check testFile.getSyncStatus() and read if it's ready
        }
    });
    // Check if testFile.getSyncStatus() is ready already to ensure nothing
    // was missed while adding the listener
}

You can also check if the version you have is the most recent version of the file using isLatest. If the file is not the latest, use DbxFile.getNewerStatus() to get the sync status for the newest version of the file. When a file is open, the Sync API will automatically download newer versions of the file as they're available and getNewerStatus() will include the download progress. Once a newer version is downloaded, getNewerStatus().isCached will be set. Then you can call update() to update the open file to the newest version.

In addition to a single file, you can also listen to changes to all files in a folder by using a DbxFileSystem.PathListener. You can also monitor the overall status of background sync, which you might use to display a status icon when the SDK is uploading or downloading.

dbxFs.addSyncStatusListener(new DbxFileSystem.SyncStatusListener() {
    @Override
    public void onSyncStatusChange(DbxFileSystem fs) {
        DbxSyncStatus fsStatus = fs.getSyncStatus();
        if (fsStatus.anyInProgress()) {
            // Show syncing indictor
        }
    }
    // Set syncing indicator based on current sync status
});

You should avoid having files open or listeners registered while your app isn't on the screen. Both are hints to the Sync API that you're actively watching for changes, and will cause it to update more frequently in the background, which consumes power. Use your onPause() method to remove listeners and close files, then use onResume() to restore them as necessary.