Can someone provide an up-to-date Android guide for Google Drive REST API v3?
Myself and many others have been struggling with setting up the Google Drive REST API v3 to work with Android apps. This mainly stems from the fact that the official Google documentation is missing a proper quick start guide for Android and we are left with finding scraps of (outdated and/or confusing) information dotted around - but what is needed is a complete up to date guide aimed at beginners to get us up and running so that they can open and edit files on their Drive, including how to set up credentials, dependencies, and manifests.
So I am asking if anyone would be willing to create such a guide, or can point to such a guide that has already been made that is a) relevant to the latest version of Google Drive API REST v3 detailed here and b) covers ALL above aspects that a beginner would need to be get started?
The guidelines posted by ArtOfWarfare here are absolutely perfect and exactly what I'm looking for - but are unfortunately out of date by several years. Can anyone provide an up-to-date version of this guide? Thank you kindly.
Before answering this question I want you to know that I got the code from here (https://ammar.lanui.online/integrate-google-drive-rest-api-on-android-app-bc4ddbd90820) and the documentation from Google was not much helpful for me. So this solution is from limited resources available to me.
I need the drive to upload and download files from my app. In drive I have to create a folder and I have to upload file from my app to that folder and download a file from the folder to my device. This code was working fine for me.
I believe that you must have completed Google login. If you don’t, go checkout this video (https://youtu.be/t-yZUqthDMM) .
To interact with the Drive API, you need to enable the Drive API service for your app. You can do this in Google Developer Console.
To enable the Drive API, complete these steps:
Go to the Google API Console.
Select a project.
In the sidebar on the left, expand APIs & auth and select APIs.
In the displayed list of available APIs, click the Drive API link and click Enable API.
If you completed it, then go to OAuth Consent screen in console and add the two scopes for drive and save it.
In your project add the dependencies below.
implementation 'com.google.android.gms:play-services-auth:17.0.0'// for google sign in
// for drive integration
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
implementation('com.google.api-client:google-api-client-android:1.26.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0')
{
exclude group: 'org.apache.httpcomponents'
}
And inside android tag, in the same gradle file, add the packaging options.
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/notice.txt'
exclude 'META-INF/ASL2.0'
}
In your Manifest file, add the required permissions
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Here I am storing the downloaded file in external storage. So that’s why I added the permissions for External storage READ and WRITE
After Google sign In, ask permission to access Google drive. The code for it is given below.
private void checkForGooglePermissions() {
if (!GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(getApplicationContext()),
ACCESS_DRIVE_SCOPE,
SCOPE_EMAIL)) {
GoogleSignIn.requestPermissions(
MainActivity.this,
RC_AUTHORIZE_DRIVE,
GoogleSignIn.getLastSignedInAccount(getApplicationContext()),
ACCESS_DRIVE_SCOPE,
SCOPE_EMAIL);
} else {
Toast.makeText(this, "Permission to access Drive and Email has been granted", Toast.LENGTH_SHORT).show();
driveSetUp();
}
}
The variables ACCESS_DRIVE_SCOPE and SCOPE_EMAIL are,
Scope ACCESS_DRIVE_SCOPE = new Scope(Scopes.DRIVE_FILE);
Scope SCOPE_EMAIL = new Scope(Scopes.EMAIL);
After having permission and Sign In we have our GoogleSignInAccount object. With this object, create an object of GoogleAccountCredential, from which we can generate an object of Drive. The Drive object is what we needed for the communication between Google Drive.
private void driveSetUp() {
GoogleSignInAccount mAccount = GoogleSignIn.getLastSignedInAccount(MainActivity.this);
GoogleAccountCredential credential =
GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Collections.singleton(Scopes.DRIVE_FILE));
credential.setSelectedAccount(mAccount.getAccount());
googleDriveService =
new com.google.api.services.drive.Drive.Builder(
AndroidHttp.newCompatibleTransport(),
new GsonFactory(),
credential)
.setApplicationName("GoogleDriveIntegration 3")
.build();
mDriveServiceHelper = new DriveServiceHelper(googleDriveService);
}
Here you can see I created an object of DriveServiceHelper class and passed the object of Drive(googleDriveSrvice) along with it. DriveServiceHelper class is given below. I got it from here.( https://github.com/gsuitedevs/android-samples/blob/master/drive/deprecation/app/src/main/java/com/google/android/gms/drive/sample/driveapimigration/DriveServiceHelper.java?source=post_page-----bc4ddbd90820----------------------). You can use that one. I made some changes in that class for myself.
public class DriveServiceHelper {
private final Executor mExecutor = Executors.newSingleThreadExecutor();
private final Drive mDriveService;
private final String TAG = "DRIVE_TAG";
public DriveServiceHelper(Drive driveService) {
mDriveService = driveService;
}
/**
* Creates a text file in the user's My Drive folder and returns its file ID.
*/
public Task<GoogleDriveFileHolder> createFile(String folderId, String filename) {
return Tasks.call(mExecutor, () -> {
GoogleDriveFileHolder googleDriveFileHolder = new GoogleDriveFileHolder();
List<String> root;
if (folderId == null) {
root = Collections.singletonList("root");
} else {
root = Collections.singletonList(folderId);
}
File metadata = new File()
.setParents(root)
.setMimeType("text/plain")
.setName(filename);
File googleFile = mDriveService.files().create(metadata).execute();
if (googleFile == null) {
throw new IOException("Null result when requesting file creation.");
}
googleDriveFileHolder.setId(googleFile.getId());
return googleDriveFileHolder;
});
}
// TO CREATE A FOLDER
public Task<GoogleDriveFileHolder> createFolder(String folderName, @Nullable String folderId) {
return Tasks.call(mExecutor, () -> {
GoogleDriveFileHolder googleDriveFileHolder = new GoogleDriveFileHolder();
List<String> root;
if (folderId == null) {
root = Collections.singletonList("root");
} else {
root = Collections.singletonList(folderId);
}
File metadata = new File()
.setParents(root)
.setMimeType("application/vnd.google-apps.folder")
.setName(folderName);
File googleFile = mDriveService.files().create(metadata).execute();
if (googleFile == null) {
throw new IOException("Null result when requesting file creation.");
}
googleDriveFileHolder.setId(googleFile.getId());
return googleDriveFileHolder;
});
}
public Task<Void> downloadFile(java.io.File targetFile, String fileId) {
return Tasks.call(mExecutor, () -> {
// Retrieve the metadata as a File object.
OutputStream outputStream = new FileOutputStream(targetFile);
mDriveService.files().get(fileId).executeMediaAndDownloadTo(outputStream);
return null;
});
}
public Task<Void> deleteFolderFile(String fileId) {
return Tasks.call(mExecutor, () -> {
// Retrieve the metadata as a File object.
if (fileId != null) {
mDriveService.files().delete(fileId).execute();
}
return null;
});
}
// TO LIST FILES
public List<File> listDriveImageFiles() throws IOException{
FileList result;
String pageToken = null;
do {
result = mDriveService.files().list()
/*.setQ("mimeType='image/png' or mimeType='text/plain'")This si to list both image and text files. Mind the type of image(png or jpeg).setQ("mimeType='image/png' or mimeType='text/plain'") */
.setSpaces("drive")
.setFields("nextPageToken, files(id, name)")
.setPageToken(pageToken)
.execute();
pageToken = result.getNextPageToken();
} while (pageToken != null);
return result.getFiles();
}
// TO UPLOAD A FILE ONTO DRIVE
public Task<GoogleDriveFileHolder> uploadFile(final java.io.File localFile,
final String mimeType, @Nullable final String folderId) {
return Tasks.call(mExecutor, new Callable<GoogleDriveFileHolder>() {
@Override
public GoogleDriveFileHolder call() throws Exception {
// Retrieve the metadata as a File object.
List<String> root;
if (folderId == null) {
root = Collections.singletonList("root");
} else {
root = Collections.singletonList(folderId);
}
File metadata = new File()
.setParents(root)
.setMimeType(mimeType)
.setName(localFile.getName());
FileContent fileContent = new FileContent(mimeType, localFile);
File fileMeta = mDriveService.files().create(metadata,
fileContent).execute();
GoogleDriveFileHolder googleDriveFileHolder = new
GoogleDriveFileHolder();
googleDriveFileHolder.setId(fileMeta.getId());
googleDriveFileHolder.setName(fileMeta.getName());
return googleDriveFileHolder;
}
});
}
}
Remember the fact that whenever you create a file or folder or if you upload a file, the drive will give a unique id for it and you can access it. So it’s not the file name that is unique in here, it’s the id of the file. Hence if you upload or create a file of same name multiple times it will be saved in the folder multiple times. So if you want to replace a file with another file of the same name. First delete the file and save/ upload it. To create a file, specify the folder id and file name to be created.
The GoogleDriveHolder class is given below.
public class GoogleDriveFileHolder {
private String id;
private String name;
private DateTime modifiedTime;
private long size;
private DateTime createdTime;
private Boolean starred;
public DateTime getCreatedTime() {
return createdTime;
}
public void setCreatedTime(DateTime createdTime) {
this.createdTime = createdTime;
}
public Boolean getStarred() {
return starred;
}
public void setStarred(Boolean starred) {
this.starred = starred;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DateTime getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(DateTime modifiedTime) {
this.modifiedTime = modifiedTime;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}
From your activity you have to call these methods. Like in the codes given below.
To create a folder
public void createFolderInDrive(View view) {
Log.i(TAG, "Creating a Folder...");
mDriveServiceHelper.createFolder("My Foder", null)
.addOnSuccessListener(new OnSuccessListener<GoogleDriveFileHolder>() {
@Override
public void onSuccess(GoogleDriveFileHolder googleDriveFileHolder) {
Gson gson = new Gson();
Log.i(TAG, "onSuccess of Folder creation: " + gson.toJson(googleDriveFileHolder));
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.i(TAG, "onFailure of Folder creation: " + e.getMessage());
}
});
}
To List files
public void listFilesInDrive(View view) {
Log.i(TAG, "Listing Files...");
new MyAsyncTask().execute();
}
To list the files, you can’t do it from your main thread because it will cause a deadlock. You have to do it in doInBackground() method of Asynctask. Here is my class.
public class MyAsyncTask extends AsyncTask<Void, Void, List<File>> {
List<File> fileList;
@Override
protected List<File> doInBackground(Void... voids) {
try {
fileList = mDriveServiceHelper.listDriveImageFiles();
} catch (IOException e) {
Log.i(TAG, "IO Exception while fetching file list");
}
return fileList;
}
@Override
protected void onPostExecute(List<File> files) {
super.onPostExecute(files);
if (files.size() == 0){
Log.i(TAG, "No Files");
}
for (File file : files) {
Log.i(TAG, "\nFound file: File Name :" +
file.getName() + " File Id :" + file.getId());
}
}
}
To Upload a file
To upload a file into Drive folder, specify the folder id , mime type of file to be uploaded and the file itself. Here I select an Image from gallery and uploaded it into drive.
public void uploadFile(View view) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_PICK_IMAGE);
} else {
Intent i = new Intent(
Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, RESULT_LOAD_IMAGE);
}
}
In onActivityResult
else if (requestCode == RESULT_LOAD_IMAGE) {
if (resultCode == RESULT_OK) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
cursor.close();
uploadImageIntoDrive(BitmapFactory.decodeFile(picturePath));
} else {
Toast.makeText(this, "Did not select any image", Toast.LENGTH_SHORT).show();
}
uploadImageIntoDrive() method,
private void uploadImageIntoDrive(Bitmap bitmap) {
try {
if (bitmap == null) {
Log.i(TAG, "Bitmap is null");
return;
}
java.io.File file = new java.io.File(getApplicationContext().getFilesDir(), "FirstFile");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos);
byte[] bitmapdata = bos.toByteArray();
//write the bytes in file
FileOutputStream fos = new FileOutputStream(file);
fos.write(bitmapdata);
fos.flush();
fos.close();
mDriveServiceHelper.uploadFile(file, "image/jpeg", "MY_FOLDER_ID")
.addOnSuccessListener(new OnSuccessListener<GoogleDriveFileHolder>() {
@Override
public void onSuccess(GoogleDriveFileHolder googleDriveFileHolder) {
Log.i(TAG, "Successfully Uploaded. File Id :" + googleDriveFileHolder.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.i(TAG, "Failed to Upload. File Id :" + e.getMessage());
}
});
} catch (Exception e) {
Log.i(TAG, "Exception : " + e.getMessage());
}
}
To Download a file
To download a file, specify the id of the file and the target file into which the downloading file has to be stored.
public void downloadFile(View view) {
java.io.File file = new java.io.File(getExternalFilesDir(null), "DemoFile2.jpg");
mDriveServiceHelper.downloadFile(file, "MY_FILE_ID")
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "Downloaded the file");
long file_size = file.length() / 1024;
Log.i(TAG, "file Size :" + file_size);
Log.i(TAG, "file Path :" + file.getAbsolutePath());
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.i(TAG, "Failed to Download the file, Exception :" + e.getMessage());
}
});
}
To Delete a file.
public void deleteFile(View view) {
mDriveServiceHelper.deleteFolderFile("MY_FILE_OR_FOLDER_ID")
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "onSuccess of Deleting File ");
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.i(TAG, "onFailure on Deleting File Exception : " + e.getMessage());
}
});
}
I am not an experienced guy. The reason I posted this code is somebody will find it useful and can bring up their own changes to it and post it here. Because there is not much reference for Drive Rest API integration for Android right now.
Thank You.
I need that as well. I managed to build something that works though not optimal from these links :
Google guides for REST API v3
Google Github demo project for migration to REST after deprecation of the other method
Documentation for REST API v3
My main remaining problem now is to find a file/folder picker. The one in the demo project uses SAF which does not allow to retrieve the ID of the file you picked (Oo !!!)
The article that I referred to while trying to figure out how to use the Drive REST API was on this page
I'm pretty new to Android, but here's how I get the list of files' IDs. Hope this helps you
create a method that returns a list of Files (do not mix them up with java.io.Files).
They are instances of com.google.api.services.drive.model.File;
The method below is a part of DriveServiceHelper class from the deprecation tutorial on github. Check the source files to see how mExecutor and mDriveService instances get created
public Task<FileList> queryFiles() {
return Tasks.call(mExecutor, () ->
mDriveService.files().list().setSpaces("drive").execute());
}
then you can iterate the list and get IDs of each file
for (File file : fileList.getFiles()) {
file.getId()
}
Once you get the ID, you can remove or update files here's an example of a method for removing duplicate files your app will create each time you make an upload to google drive:
private void mQuery(String name) {
if (mDriveServiceHelper != null) {
Log.d(TAG, "Querying for files.");
mDriveServiceHelper.queryFiles()
.addOnSuccessListener(fileList -> {
for (File file : fileList.getFiles()) {
if(file.getName().equals(name))
mDriveServiceHelper.deleteFolderFile(file.getId()).addOnSuccessListener(v-> Log.d(TAG, "removed file "+file.getName())).
addOnFailureListener(v-> Log.d(TAG, "File was not removed: "+file.getName()));
}
})
.addOnFailureListener(exception -> Log.e(TAG, "Unable to query files.", exception));
}
}
and here's the deleteFolderFile method from DriveServiceHelper class
public Task<Void> deleteFolderFile(String fileId) {
return Tasks.call(mExecutor, () -> {
// Retrieve the metadata as a File object.
if (fileId != null) {
mDriveService.files().delete(fileId).execute();
}
return null;
});
}
NB! this is not the best approach if you need to perform a query on a large list of files. It's just a draft to help you get an idea. At least mQuery func can be improved by utilizing a binary search algorithm to look up a particular file in a list.