Android Drive NPE upload file [duplicate] - android

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
I'm trying to send the database file to Google Drive, I imported the libreries necessary, but when I try to upload the file to GoogleDrive I get NPE.
public class Main extends BaseDemoActivity {
private Drive service;
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
// create new contents resource
com.google.android.gms.drive.Drive.DriveApi.newDriveContents(getGoogleApiClient())
.setResultCallback(driveContentsCallback);
}
final private ResultCallback<DriveContentsResult> driveContentsCallback = new
ResultCallback<DriveContentsResult>() {
#Override
public void onResult(final DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
new Thread() {
#Override
public void run() {
try {
java.io.File externalDB = new java.io.File(Environment.getExternalStorageDirectory(), getString(R.string.app_name) + "/data.crypt");
java.io.File fileContent = new java.io.File(externalDB.getPath());
FileContent mediaContent = new FileContent("application/x-sqlite3", fileContent);
com.google.api.services.drive.model.File body =
new com.google.api.services.drive.model.File();
body.setTitle("data.crypt");
body.setMimeType("application/x-sqlite3");
com.google.api.services.drive.model.File file =
service.files().insert(body, mediaContent).execute();
if (file != null) {
Log.d("GoogleDrive", "File Uploaded");
}
} catch (UserRecoverableAuthIOException e) {
//startActivityForResult(e.getIntent(), REQUEST_AUTHORISATION);
} catch (IOException e) {
e.printStackTrace();
}
}
}
.start();
}
};
...
...
The line of NPE
com.google.api.services.drive.model.File file =
service.files().insert(body, mediaContent).execute();
Log
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: FATAL EXCEPTION: Thread-34061
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: Process: chris.con, PID: 11372
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.api.services.drive.Drive$Files com.google.api.services.drive.Drive.files()' on a null object reference
01-17 11:48:32.387 11372-12201/? E/AndroidRuntime: at chris.con.Main$1$1.run(Main.java:51)

Well, the log message tells you that your private Drive service is not initialized.
The main issue here is that you're getting into a mess because you are mixing two independent APIs to work with Drive. One is the one from Google Play Sevices with package com.google.android.gms.drive and the other one is from the google-api-java with package com.google.api.services.drive. You should stick to only one of them and on my experience it should be the one of com.google.api.services. The one from Play Services has more bugs, less functionality, and you will often hit rate limits if you want data to be synced when you so require it instead of when the library feels like syncing the data.
You initialize the api.services Drive like this (for example)
GoogleAccountCredential credential = new GoogleAccountCredential(this, "oauth2:" + DriveScopes.DRIVE_FILE);
mDrive = new Drive.Builder(
new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
.setApplicationName("Your App")
.build();
After that you can service.files().insert. You don't need to "create contents" first with the com.google.api.services API.

Related

Android app: upload file to google drive, share link, download file

My Android app should offer the functionality of sharing files via google drive:
1) upload a file (which was selected previously from the sd-card) to google drive
2) get back a link (url) to the uploaded file
3) share this link with other users of the app
4) other users may download the shared file to the sd-card of their device
All this functionality should be available in the app, without having the need to use a browser.
Does anyone have an idea how i can implement the steps 1, 2 and 4?
thanks in advance!
gerhard
This can help you for Google Drive file upload -
First, go for authentication
AccountManager am = AccountManager.get(activity);
am.getAuthToken(am.getAccounts())[0],
"oauth2:" + DriveScopes.DRIVE,
new Bundle(),
true,
new OnTokenAcquired(),
null);
Now need to set token
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
try {
final String token = result.getResult().getString(AccountManager.KEY_AUTHTOKEN);
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
Drive.Builder b = new Drive.Builder(httpTransport, jsonFactory, null);
b.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
#Override
public void initialize(JSonHttpRequest request) throws IOException {
DriveRequest driveRequest = (DriveRequest) request;
driveRequest.setPrettyPrint(true);
driveRequest.setKey(CLIENT ID YOU GOT WHEN SETTING UP THE CONSOLE BEFORE YOU STARTED CODING)
driveRequest.setOauthToken(token);
}
});
final Drive drive = b.build();
final com.google.api.services.drive.model.File body = new
com.google.api.services.drive.model.File();
body.setTitle("My Test File");
body.setDescription("A Test File");
body.setMimeType("text/plain");
final FileContent mediaContent = new FileContent("text/plain",
"Your data")
new Thread(new Runnable() {
public void run() {
try {
com.google.api.services.drive.model.File file =
drive.files().insert(body, mediaContent).execute();
alreadyTriedAgain = false;
} catch (IOException e) {
if (!alreadyTriedAgain) {
alreadyTriedAgain = true;
AccountManager am = AccountManager.get(activity);
am.invalidateAuthToken(am.getAccounts()[0].type, null); // Requires the permissions MANAGE_ACCOUNTS & USE_CREDENTIALS in the Manifest
am.getAuthToken (same as before...)
} else {
// Give up. Crash or log an error or whatever you want.
}
}
}
}).start();
Intent launch = (Intent)result.getResult().get(AccountManager.KEY_INTENT);
if (launch != null) {
startActivityForResult(launch, 3025);
return; // Not sure why... I wrote it here for some reason. Might not actually be necessary.
}
} catch (OperationCanceledException e) {
// Handle it...
} catch (AuthenticatorException e) {
// Handle it...
} catch (IOException e) {
// Handle it...
}
}
}
Now, To update the file
public void updateFile(Drive drive, File gFile, java.io.File jFile) throws
IOException {
FileContent gContent = new FileContent("text/csv", jFile);
gFile.setModifiedDate(new DateTime(false, jFile.lastModified(), 0));
gFile = drive.files().update(gFile.getId(), gFile,
gContent).setSetModifiedDate(true).execute();
}
Also, Don't fget to give permissions in Manifest for
GET_ACCOUNTS, USE_CREDENTIALS, MANAGE_ACCOUNTS, INTERNET WRITE_EXTERNAL_STORAGE

Google Drive API - Android - How to obtain a drive file id?

I'm trying to develop an android app that can read a xml file stored in my google drive folder, the idea at first is trying to open the file and handle the content.
I've read the Google Drive API docs for android and i reached a point that I'm lost, it's working with file contents.
According to this guide the way to open a file from drive is this:
DriveFile file = ...
file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(contentsOpenedCallback);`
Searching I realized that the complete code (that they not include there is):
DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient,DriveId.bg(id));
file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(contentsOpenedCallback);`
Well the problem there is that I don't know the file "id". I've tried the id from the web link of google drive, something like this (https://drive.google.com/open?id=1EafJ-T6H4xI9VaUuUO5FMVb9Y30xyr7OHuISQ53avso&authuser=0) but didnĀ“t work.
You could use the DriveAPI Query method, to retrieve any information about an specific file. you will need to define a query object as the following:
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.TITLE, "HelloWorld.java"))
.build();
And set a callback function to iterate on the results:
Drive.DriveApi.query(googleApiClient, query)
.setResultCallback(new OnChildrenRetrievedCallback() {
#Override
public void onChildrenRetrieved(MetadataBufferResult result) {
// Iterate over the matching Metadata instances in mdResultSet
}
});
You can find more information on the topic here: https://developers.google.com/drive/android/queries
The solution i found for this problem was creating the file from the app. Using the class ("CreateFileActivity.java") from google drive api demo app.
With this class i save the returning Driveid from the new file in a global DriveId variable.
final private ResultCallback<DriveFolder.DriveFileResult> fileCallback = new
ResultCallback<DriveFolder.DriveFileResult>() {
#Override
public void onResult(DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.e("","Error while trying to create the file");
return;
}
Id=result.getDriveFile().getDriveId();
Log.e("","Created a file with content: " + Id);
}
};
Then with this id in another method i call the file and read it (If i want i can edit this file information from Google Drive Web App):
public void leer(){
DriveFile file = Drive.DriveApi.getFile(getGoogleApiClient(),Id);
file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(contentsOpenedCallback);
}
ResultCallback<DriveApi.DriveContentsResult> contentsOpenedCallback =
new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
Log.e("Error:","No se puede abrir el archivo o no se encuentra");
return;
}
// DriveContents object contains pointers
// to the actual byte stream
DriveContents contents = result.getDriveContents();
BufferedReader reader = new BufferedReader(new InputStreamReader(contents.getInputStream()));
StringBuilder builder = new StringBuilder();
String line;
try {
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
String contentsAsString = builder.toString();
Log.e("RESULT:",contentsAsString);
}
};
I've been playing with this stuff a few months back, and still have some code on GitHub. It may be VERY outdated (libver 15 or so), but it may serve as a reference point, and it is simple. Look here. Pull it down, plug in, step through. Fix what's not working anymore :-). I've abandoned it some time ago.
Be aware of the fact that there are 2 different IDs for Google Drive Android API objects, see SO 22841237.
In general, you usually start with knowing the file/folder name, query GDAA to get a list of objects. Each of them will yield DriveID and ResourceID. DriveID is used in your app to manipulate the objects (does not mean anything outside your Android App and/or device). ResourceID is the string that appears in different forms in URLs and can be used outside your app (web browser for instance...). Look at this wrapper to get some feeling how it works. But again, it's been a few versions back, so there are no guaranties.
The Google Drive API is deprecated, now its Google Drive V3 and for Query we use
String pageToken = null;
do {
FileList result = driveService.files().list()
.setQ("mimeType='image/jpeg'")
.setSpaces("drive")
.setFields("nextPageToken, files(id, name)")
.setPageToken(pageToken)
.execute();
for (File file : result.getFiles()) {
System.out.printf("Found file: %s (%s)\n",
file.getName(), file.getId());
}
pageToken = result.getNextPageToken();
}
while (pageToken != null);
You can Learn more here Officals Docs

Upload file to google drive fails

Upload a file from android to Google drive is failing. Seems like the createFile is not working even the status returned is success. The actual status error code and message that I get from the Google API is error code 8, error message: Provided DriveId is not valid
I also cannot find the file on the related user Google drive (using web interface for the Google drive).
I have also tried using Drive.DriveApi.fetchDriveId(m_api, m_file.getDriveId().toString()) in the create file callback (instead of calling to openContents), and it has also failed.
Related code is below, any help would be appreciated!
public void createFile() {
Drive.DriveApi.newContents(m_api).setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (handleError(result.getStatus())) {
return;
}
Contents contents = result.getContents();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("aaab").setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, contents)
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (handleError(result.getStatus())) {
return;
}
m_file = result.getDriveFile();
m_file.openContents(m_api, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(updateCallback);
}
};
final private ResultCallback<ContentsResult> updateCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
// Getting error here
if (handleError(result.getStatus())) {
return;
}
...
OK problem is that new file is not created.
for creating new file on GoogleDrive i use this code and work fine, problem is in your googleapiclient or i don't know where and how you call your funct createFile(). You must call in onConnected metode.
#Override
public void onConnected(Bundle connectionHint) {
super.onConnected(connectionHint);
// create new contents resource
Drive.DriveApi.newContents(m_api)
.setResultCallback(contentsCallback);
}
final private ResultCallback<ContentsResult> contentsCallback = new
ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create new file contents");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("New file")
.setMimeType("text/plain")
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(m_api)
.createFile(m_api, changeSet, result.getContents())
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new
ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the file");
return;
}
showMessage("Created a file: " + result.getDriveFile().getDriveId());
}
};
I've fixed it!
It had 2 problems:
First issue is my fault - my android manifest was missing APP_ID for drive services:
<meta-data
android:name="com.google.android.apps.drive.APP_ID"
android:value="#string/app_id" />
Second, the activity was extending predefined Google class: BaseGameActivity.
But this class assumes the I am not using Google drive files. To make it work, I had to add this in my Activity:
mRequestedClients = GameHelper.CLIENT_ALL;
and change Google predefined code. Actually it looks like a bug in google, assuming google APP folder is always used, and not Google drive files. In GameHelper.java, line 298, from:
builder.addScope(Drive.SCOPE_APPFOLDER);
to:
builder.addScope(Drive.SCOPE_FILE);

Create or Open native Google documents using GoogleDriveApi

I have been closely following the documentation for the Google Drive Android API and, all works great. I can create new text documents and read them back in using the mime type of text/plain.
What I cannot do is create a native Google "Document" or "Spreadsheet." Actually, I can create them by using the mime type to application/vnd.google-apps.document or application/vnd.google-apps.spreadsheet as per Supported MIME Types documentation.
If, however, I try to write content to these documents, the documents never get uploaded.
If I try to read documents that have content (content I created via a web browser) my openContents call fails.
Again, I can create text/plain documents and write to them, but they are not native Google Documents. I have scowered the documentation and sample files, but nothing describes what I'm looking for.
This seems so basic. Does the new GoogleApiClient not support doing this? What am I missing or doing wrong?
Here is the core code for creating. I have a similar issue when trying to read a application/vnd.google-apps.document but I'm sure the two issues are related. I'll spare the verbosity of "read" code.
private void exportToGDriveFile() {
Drive.DriveApi.newContents(getGoogleApiClient()).setResultCallback(createNewFileCallback);
}
final private ResultCallback<ContentsResult> createNewFileCallback = new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create new file contents");
return;
}
String fileName = getIncrementedFileName();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(fileName)
.setMimeType("text/plain") // <-- This works! I can write and read back :)
//.setMimeType("application/vnd.google-apps.document") <-- can create if no contents are included.
//.setMimeType("application/vnd.google-apps.spreadsheet")
.setStarred(true)
.build();
writeLog("creating file: " + fileName);
// create a file on root folder
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFile(getGoogleApiClient(), changeSet, result.getContents())
.setResultCallback(afterCreateFileCallback);
}
};
private ResultCallback<DriveFileResult> afterCreateFileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
writeLog("Error while trying to create the file");
return;
}
DriveFile driveFile = result.getDriveFile();
writeLog("Created file " + driveFile.getDriveId());
new WriteFileAsyncTask().execute(driveFile);
}
};
private class WriteFileAsyncTask extends AsyncTask<DriveFile, Void, Boolean> {
#Override
protected Boolean doInBackground(DriveFile... args) {
DriveFile file = args[0];
try {
ContentsResult contentsResult = file.openContents(getGoogleApiClient(), DriveFile.MODE_WRITE_ONLY, null).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
/************************
If I try to write content here, `application/vnd.google-apps.document` files will not upload.
*************************/
String contents = "Hello World";
OutputStream outputStream = contentsResult.getContents().getOutputStream();
outputStream.write(contents.getBytes());
com.google.android.gms.common.api.Status status = file.commitAndCloseContents(
getGoogleApiClient(), contentsResult.getContents()).await();
return status.getStatus().isSuccess();
} catch (IOException e) {
// toast("IOException while appending to the output stream");
}
return false;
}
#Override
protected void onPostExecute(Boolean result) {
if (!result) {
// toast("Error while editing contents");
return;
}
// toast("Successfully uploaded Quizifications!");
}
}
It's not currently possible to read or edit the contents of Google Documents, Spreadsheets or Presentation files. They files are of a special type that don't have standard binary content, so you can't read and write from them in the same way you can from other files.
You can, however, interact with the metadata of existing files.
Sorry for the confusion, we should update the behavior so that its clear that its not possible.
Updating Google Docs with HTML is simple. Just make an api request with html-formatted text in the body (html tag is required) and content-type to be google docs, then your created/updated file will be available to the user as a Google Doc with all the formatting options.
request({
uri: 'https://www.googleapis.com/upload/drive/v2/files/'+fileId,
method: 'PUT',
qs: {
uploadType: 'media'
},
form: '<html> Hello <b>World!</b> </html>',
headers: {
'Content-Type': 'application/vnd.google-apps.document',
'Authorization': 'Bearer ' + access_token
}}, function (error, response, body){
})

Drive SDK for Android AuthIntent is null

I'm trying to integrate Google Drive into my Android app so that I can store files in a user's Drive. I've enabled both Drive SDK and Drive API in the App Console and set up my Oauth 2 ClientIDs with my SHA1 fingerprint. As far as I can tell, everything looks good on Google's end.
Here's the code that initializes my Drive object.
private boolean initializeService(String accountName) {
if(service != null) {
return true;
}
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(mContext, DriveScopes.DRIVE);
mAuthIntent = null;
if(accountName != null) {
try {
credential.setSelectedAccountName(accountName);
Log.d("DriveHelper", credential.getToken());
service = getDriveService(credential);
} catch(Exception ex) {
if(ex instanceof UserRecoverableAuthException) {
UserRecoverableAuthException authException = (UserRecoverableAuthException)ex;
mAuthIntent = authException.getIntent();
mErrorCode = CloudHelper.CONNECTION_AUTHENTICATE;
((Activity)mContext).startActivityForResult(mAuthIntent, REQUEST_AUTHORIZATION);
} else {
Log.e("DriveCloudHelper", "Error retrieving auth token", ex);
}
return false;
}
getAppFolder();
return mFileLocation != null;
}
Activity activity = (Activity)mContext;
activity.startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
return false;
}
private Drive getDriveService(GoogleAccountCredential credential) {
return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential)
.setApplicationName("Drive Helper")
.build();
}
These methods are called on a background thread and work just fine, the Account Picker appears, the Authorization window appears, all good. My getAppFolder method looks for a specific folder and creates it if not present without any issue (on the same background thread).
However, when I go to upload a file to Drive, the first operation I try on a new background thread causes a UserRecoverableAuthException, but a new exception occurs when I call getIntent because there's no Intent provided, and the detail message is AppDownloadRequired.
Here's the method that's generating the exception.
private String getIdFromFolder(String location, String name, boolean create) {
try {
String maskedName = name.replaceAll("/", "");
String query = "title contains '" + maskedName + "' and '" + location + "' in parents";
FileList list = service.files().list().setMaxResults(1).setQ(query).execute();
List<File> files = list.getItems();
if (files.isEmpty()) {
return create ? createSubFolder(location, name) : null;
}
return files.get(0).getId();
} catch (IOException e) {
// Eat the exception here
Log.w("DriveCloudHelper", e);
}
return create ? createSubFolder(location, name) : null;
}
And here's the stacktrace:
com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:222)
at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:836)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:412)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:345)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:463)
at net.wishfullthinking.groceryhelper.interfaces.DriveCloudHelper.getIdFromFolder(DriveCloudHelper.java:269)
at net.wishfullthinking.groceryhelper.interfaces.DriveCloudHelper.uploadDatabaseToCloud(DriveCloudHelper.java:405)
at net.wishfullthinking.groceryhelper.interfaces.DriveCloudHelper.synchFileToCloud(DriveCloudHelper.java:368)
at net.wishfullthinking.groceryhelper.interfaces.CloudHelper.mergeCloudStoreCollection(CloudHelper.java:472)
at net.wishfullthinking.groceryhelper.interfaces.CloudHelper.access$10(CloudHelper.java:454)
at net.wishfullthinking.groceryhelper.interfaces.CloudHelper$4.run(CloudHelper.java:444)
at java.lang.Thread.run(Thread.java:856)
Caused by: com.google.android.gms.auth.UserRecoverableAuthException: AppDownloadRequired
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential.getToken(GoogleAccountCredential.java:192)
at com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential$RequestHandler.intercept(GoogleAccountCredential.java:217)
... 11 more
Try using mContext.getApplicationContext() in your GoogleAccountCredential.usingOAuth2 call rather than mContext as that seems to have fixed similar issues elsewhere.

Categories

Resources