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.
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:
DriveId driveId = metadata.getDriveId();
String resourceId = driveId.getResourceId();
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
The Activity
:
public class GoogleDriveActivity extends Activity implements
GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
private GoogleApiClient mGoogleApiClient;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
public static final int REQUEST_CODE_RESOLUTION = 1;
public static final int REQUEST_CODE_SELECT = 2;
private static final String[] SCOPES = { DriveScopes.DRIVE_FILE};
private static final String TAG = "GoogleDrive";
private String accountName;
@Override
protected void onResume() {
super.onResume();
setupGoogleClient();
}
@Override
protected void onPause() {
super.onPause();
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
}
@Override
public void onConnected(Bundle bundle) {
IntentSender intentSender = Drive.DriveApi
.newOpenFileActivityBuilder()
.build(mGoogleApiClient);
AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
Account[] list = manager.getAccountsByType("com.google");
//Getting the first account because that is the primary account for that user
accountName = list[0].name;
try {
startIntentSenderForResult(intentSender, REQUEST_CODE_SELECT, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, "Unable to send intent to connect to Google API client " + e.getMessage());
}
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
if (!connectionResult.hasResolution()) {
return;
}
try {
connectionResult.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (IntentSender.SendIntentException e) {
Log.w(TAG, "Unable to send intent to connect to Google API client " + e.getMessage());
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CODE_SELECT:
if (resultCode == RESULT_OK) {
DriveId driveId = data.getParcelableExtra(OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
String resourceId = driveId.getResourceId();
new GoogleDriveAsync(this).execute(resourceId);
}
finish();
break;
case REQUEST_CODE_RESOLUTION:
if (resultCode == RESULT_OK) {
mGoogleApiClient.connect();
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
@Override
public void onConnectionSuspended(int i) {
Log.w(TAG, "Connection to Google API client suspended");
}
private void setupGoogleClient() {
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
mGoogleApiClient.connect();
}
class GoogleDriveAsync extends AsyncTask<String, Void, Void> {
private GoogleDriveActivity activity;
GoogleDriveAsync(GoogleDriveActivity activity) {
this.activity = activity;
}
@Override
protected Void doInBackground(String... args) {
try {
String id = args[0];
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES));
credential.setBackOff(new ExponentialBackOff());
credential.setSelectedAccountName(accountName);
com.google.api.services.drive.Drive service = new com.google.api.services.drive.Drive.Builder(transport, jsonFactory, credential).build();
File file = service.files().get(id).setFields("downloadUrl").execute();
if (file != null) {
String strUrl = file.getDownloadUrl();
String token = GoogleAuthUtil.getToken(activity, accountName, "oauth2: " + Drive.SCOPE_FILE);
//This is your downloadUrl and token
Log.w(TAG, "download link is " + strUrl + " and token is " + token);
}
} catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
Log.w(TAG, "Google Play Services not available to get downloadUrl of selected file");
} catch (UserRecoverableAuthIOException userRecoverableException) {
Log.w(TAG, "User authorization error in getting downloadUrl " + userRecoverableException.getMessage());
} catch (Exception e) {
Log.w(TAG, "Exception in getting downloadUrl " + e.getMessage());
}
return null;
}
}
}
The AndroidManifest
(relevant lines) :
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<activity
android:name="com.myapp.GoogleDriveActivity"
android:label="@string/app_name"
<meta-data android:name="com.google.android.apps.drive.APP_ID" android:value="id=<your-gogole-project-id>"/>
</activity>
The build.gradle
(relevant lines):
compile 'com.google.android.gms:play-services-drive:7.8.0'
compile 'com.google.api-client:google-api-client:1.20.0'
compile 'com.google.api-client:google-api-client-android:1.20.0'
compile 'com.google.api-client:google-api-client-gson:1.20.0'
compile 'com.google.apis:google-api-services-drive:v2-rev170-1.20.0'
来源:https://stackoverflow.com/questions/32210970/list-files-from-google-drive-and-get-downloadurl-for-selected-file-in-android