I am trying to show a list of files the user has on their Google Drive from my Android app and once the user has selected a file, I want to get the downloadUrl
and the Bearer token
for that account to give it to my application server to download it.
I have been looking around and it seems very confusing. There are 2 SDKs, Android SDK and Java SDK (REST
based) for Google Drive.
I successfully was able to get the list of files and display it using the Android SDK (I did not have to build any UI), and when the user selects a file, I got all the metadata about the file but the downloadUrl
. I did get some links like the webContentLink
and the alternateLink
, but it turns out that because the file was not shared, I cannot pass these links to my server to download it.
Upon some more research I found out that the downloadUrl
is accessible by using the Java SDK. My question is, do I have to build my own UI to display a list of files I obtain? How do I handle the folder hierarchy if I have to build my UI to show these files?
Below is the code which prints the data about the File
. I have implemented this code based on the tutorial.
public class GoogleDriveActivity extends Activity {
private GoogleApiClient mGoogleApiClient;
public com.google.api.services.drive.Drive mService;
public GoogleAccountCredential credential;
public static final int REQUEST_AUTHORIZATION = 3;
public static final int REQUEST_ACCOUNT_PICKER = 4;
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = {DriveScopes.DRIVE_METADATA_READONLY};
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_drive);
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
credential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(), Arrays.asList(SCOPES))
.setBackOff(new ExponentialBackOff())
.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, "abc.test@gmail.com"));
mService = new com.google.api.services.drive.Drive.Builder(
transport, jsonFactory, credential)
.setApplicationName("My Application")
.build();
}
@Override
public void onResume() {
super.onResume();
refreshResults();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_AUTHORIZATION:
if (resultCode != RESULT_OK) {
chooseAccount();
}
break;
case REQUEST_ACCOUNT_PICKER:
Log.w("gd", "in account picker");
if (resultCode == RESULT_OK && data != null &&
data.getExtras() != null) {
String accountName =
data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
credential.setSelectedAccountName(accountName);
SharedPreferences settings =
getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.commit();
}
} else if (resultCode == RESULT_CANCELED) {
Log.W("gd", "in cancelled");
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
private void chooseAccount() {
startActivityForResult(
credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}
private void refreshResults() {
new GoogleDriveAsync(this).execute();
}
public class GoogleDriveAsync extends AsyncTask<Void, Void, Void> {
private GoogleDriveActivity activity;
@Override
protected Void doInBackground(Void... voids) {
try {
getDataFromApi();
} catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
Log.w("gd", "GPS unavailable");
} catch (UserRecoverableAuthIOException userRecoverableException) {
Log.w("gd", "user recoverable");
activity.startActivityForResult(
userRecoverableException.getIntent(),
GoogleDriveActivity.REQUEST_AUTHORIZATION);
} catch (Exception e) {
Log.w("gd", "general exception " + e.getMessage());
}
return null;
}
GoogleDriveAsync(GoogleDriveActivity activity) {
this.activity = activity;
}
/**
* Fetch a list of up to 10 file names and IDs.
*
* @return List of Strings describing files, or an empty list if no files
* found.
* @throws IOException
*/
private List<String> getDataFromApi() throws IOException {
// Get a list of up to 10 files.
List<String> fileInfo = new ArrayList<String>();
FileList result = activity.mService.files().list()
.setMaxResults(10)
.execute();
List<File> files = result.getItems();
if (files != null) {
for (File file : files) {
fileInfo.add(String.format("%s (%s) (%s)\n",
file.getTitle(), file.getId(), file.getDownloadUrl()));
}
}
Log.w("gd", "file info is " + fileInfo.toString());
return fileInfo;
}
}
}
EDIT: Please see my answer (not the accepted one) for a working sample. The use case is: list all the Google Drive files for the user and when selected one, get the downloadUrl
and the access_token
for the file.
The
Activity
:The
AndroidManifest
(relevant lines) :The
build.gradle
(relevant lines):In order to use the file/folder outside of your GDAA based app, you need so-called ResourceID. This ResourceId is a string that uniquely identifies the object of Google Drive (see here)
Turn DriveId into ResourceId:
Once you have ResourceId, you can construct 'download URL' for your server app from it. The ResourceID string is the one found if you go to drive.google.com, select a file/folder and perform rightbutton > getLink.
(looks like 'https://drive.google.com/open?id=0B1mQUW2__I_am_the_resource_id')
Just take this string '0B1mQUW2__I_am_the_resource_id' to the 'TryIt' playground here and paste it to the 'fileId' field.
So, the short answer is that you don't need RESTful Api to get the file/folder identifier you can use elsewhere.
Second part of your question, 'How do I handle the folder hierarchy if I have to build my UI to show these files?' is answered (somewhat) in the 'createTree()/testTree()' methods of MainActivity of these 2 demos (GDAADemo, RESTDemo). These are the same tasks implemented on the GDAA as well as the REST Apis and the choice depends mainly on the SCOPE your app needs (GDAA supports only the FILE scope, whereas REST supports both the FILE and the DRIVE scopes)
Good Luck