'Android : copy file to Google Drive using Google Drive API - what is best approach?

I need to copy a file to Google Drive from Android App. Consider copying 4-5 MB file size to root folder.

Have gone through some sample code from here.

Can someone please check below flow and let me know if this is the best approach. I doubt because with this approach

  1. have to use many callbacks , which makes code logic less straight forward

  2. instead of having a copy like API method, using outputStreamwriter.write() method for file copy operation

Am not saying demo I have gone through is not correct, but don't want to miss any better approach, if exists around.. it is also possible I overlooked something..and picked up wrong example..

Here is the flow I found, and want to check with some Google Drive API expert..

  1. After user authorization and connection : call newDriveContents API

Drive.DriveApi.newDriveContents(getGoogleApiClient()).setResultCallback(driveContentsCallback);

  1. DriveContentsResult Callback - Call create file , and use output stream write to create or copy files Drive.DriveApi.getRootFolder(getGoogleApiClient()).createFile(getGoogleApiClient(), changeSet, driveContents).setResultCallback(fileCallback);

driveContents will come from OutputStreamWriter.write ( file contents )

  1. DriveFileResult Callback - to check sucess or failure Drive.DriveApi.getRootFolder(getGoogleApiClient()).createFile(getGoogleApiClient(), changeSet, driveContents).setResultCallback(fileCallback);

Note : I haven't used try/catch and new Thread() { } in above pseudocode to just make it small and readable ...



Solution 1:[1]

After completing the integration of an app with Google Drive SDK, I can say this is the best approach because

1) Call to Google drive SDK have to be Asynchronous. Because it is Asynchronous , it require call back

2) for copy operation to and from Google Drive, there is no straight forward copy method available. android standard outputStreamwriter and inputStreamWriter has tobe used

There is another synchronous approach is available as shown below, but it is not recommended by Google except for delete operations

DriveApi.DriveContentsResult driveContentsResult = backupDriveFile.open(googleApiClient,DriveFile.MODE_READ_ONLY,null).await() ;

Solution 2:[2]

If it's a matter of refactoring this is how I would do it:

public class GoogleDrive implements DriveProvider  
{
   private String contents;
   private ArrayList<Result> results;
   private GoogleApiClient googleApiClient;

   public GoogleDrive(GoogleApiClient googleApiClient){
      ArrayList<Result> results = new ArrayList<Result>();
      this.googleApiClient = googleApiClient;
   }

   public ArrayList<Result> upload(String contents){
       this.contents = contents;
       Drive.DriveApi.newDriveContents(this.googleApiClient).setResultCallback(driveContentsCallback);
   }

   final private ResultCallback<DriveContentsResult> driveContentsCallback = new
        ResultCallback<DriveContentsResult>() {
    @Override
    public void onResult(DriveContentsResult result) {
        if (!result.getStatus().isSuccess()) {
            results.add(new Result("Error while trying to create new file contents", true));
            return;
        }
        final DriveContents driveContents = result.getDriveContents();

        // Perform I/O off the UI thread.
        new Thread() {
            @Override
            public void run() {
                // write content to DriveContents
                OutputStream outputStream = driveContents.getOutputStream();
                Writer writer = new OutputStreamWriter(outputStream);
                try {
                    writer.write(this.contents);
                    writer.close();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                }

                MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
                        .setTitle("New file")
                        .setMimeType("text/plain")
                        .setStarred(true).build();

                // create a file on root folder
                Drive.DriveApi.getRootFolder(this.googleApiClient)
                        .createFile(this.googleApiClient, changeSet, driveContents)
                        .setResultCallback(fileCallback);
            }
        }.start();
    }
};

final private ResultCallback<DriveFileResult> fileCallback = new
        ResultCallback<DriveFileResult>() {
    @Override
    public void onResult(DriveFileResult result) {
        if (!result.getStatus().isSuccess()) {
            results.add(new Result("Error while trying to create the file", true));
            return;
        }
        results.add(new Result("Created a file with content: " + result.getDriveFile().getDriveId(), false));
    }
};
}

public interface DriveProvider {
   public ArrayList<Result> upload(String contents);
}

public class Result{
private String message;
private boolean error;

public Result(String message, boolean error){
    this.message = message;
    this.error = error;
}

public String getMessage(){
    return this.message;
}

public boolean isError(){
    return this.error;
}
}

Usage:

@Override
public void onConnected(Bundle connectionHint) {
    super.onConnected(connectionHint);

    DriveProvider driverProvider = new GoogleDrive(getGoogleApiClient()); //This should be Injected using IoC container

    ArrayList<Result> results = driverProvider.upload("Hello World");

    for(Result result : results){
        showMessage(result.getMessage()); //isError could be used to change application flow by firing an Intent if all message are not errored.
    }
}

The classes and the interface would be better to put them in separate files. This implementation is so that your app depends on an abstraction no concrete implementation and to separate the Google Drive logic from your UI.

Hope this answers your question.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Chester Cobus