I'm having some trouble integrating the Google Drive API into my Android app. Basically, I'm trying to create a folder in which to store my error logs. The problem is that occasionally, the thread will run, and the folder won't be created by the time the file needs to be created, and so the app crashes. I'm trying to think about how to avoid this scenario, and have tried a few methods so far, including making the folder assignment in the folder callback, but all to no avail. I sometimes get lucky (particularly when debugging, unfortunately) and the file saves OK, but I obviously need to resolve the concurrency issue. Please let me know if any more code is needed for context.
protected void onPause() {
// TODO Find the right place for this
if (!mDriveLogList.isEmpty()) {
// Perform I/O off the UI thread.
// TODO Fix to use RxJava
new Thread() {
#Override
public void run() {
Timber.i("Starting File Creation");
// write content to DriveContents
if (mDriveContents == null) {
Timber.e("Drive Contents Were Null");
} else {
OutputStream outputStream = mDriveContents.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
//TODO Clean/Refactor
try {
writer.write("Status Log For: " + DateHelper.getDate() + "\n");
for (String logEntry : mDriveLogList) {
writer.write(logEntry + "\n");
}
writer.close();
} catch (IOException e) {
Timber.e(e, "Error During Drive Contents Callback");
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(SURVEIL_DROID + " " + DateHelper.getDate())
.setMimeType("text/plain")
.setStarred(true).build();
MetadataChangeSet folderSet = new MetadataChangeSet.Builder()
.setTitle("SurveilCustomFolder")
.build();
Drive.DriveApi.getRootFolder(mGoogleApiClient)
.createFolder(mGoogleApiClient, folderSet)
.setResultCallback(folderCallback);
mDriveFolder = mFolderId.asDriveFolder();
mDriveFolder.createFile(mGoogleApiClient, changeSet, mDriveContents)
.setResultCallback(fileCallback);
mDriveLogList.clear();
}
}
}.start();
You could use a countdownlatch (See https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html) to help with the synchronization. Something like
protected void onPause() {
// TODO Find the right place for this
if (!mDriveLogList.isEmpty()) {
// Perform I/O off the UI thread.
// TODO Fix to use RxJava
new Thread() {
#Override
public void run() {
final protected CountDownLatch connectionLatch = new CountDownLatch(1);
protected boolean folderCreated = false;
Timber.i("Starting File Creation");
// write content to DriveContents
if (mDriveContents == null) {
Timber.e("Drive Contents Were Null");
} else {
OutputStream outputStream = mDriveContents.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
//TODO Clean/Refactor
try {
writer.write("Status Log For: " + DateHelper.getDate() + "\n");
for (String logEntry : mDriveLogList) {
writer.write(logEntry + "\n");
}
writer.close();
} catch (IOException e) {
Timber.e(e, "Error During Drive Contents Callback");
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(SURVEIL_DROID + " " + DateHelper.getDate())
.setMimeType("text/plain")
.setStarred(true).build();
MetadataChangeSet folderSet = new MetadataChangeSet.Builder()
.setTitle("SurveilCustomFolder")
.build();
Drive.DriveApi.getRootFolder(mGoogleApiClient)
.createFolder(mGoogleApiClient, folderSet)
.setResultCallback(new
ResultCallback<DriveFolderResult>() {
#Override
public void onResult(DriveFolderResult result) {
folderCreated = result.getStatus().isSuccess();
connectionLatch.countDown(); // Release latch
}
});
try {
connectionLatch.await() ;
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
if (folderCreated) {
mDriveFolder = mFolderId.asDriveFolder();
mDriveFolder.createFile(mGoogleApiClient, changeSet, mDriveContents)
.setResultCallback(fileCallback);
mDriveLogList.clear();
}
}
}
}.start();
}
}
should work.
createFolder() returns a PendingResult, you can use one of the await() methods of the PendingResult to block until the folder is created. See here: (https://developers.google.com/android/reference/com/google/android/gms/common/api/PendingResult.html#await())
Related
I am using google drive authorization in my application. While I was running the application using android studio debug/run option, it was working fine. But, today I published the application and after installing it, I am stuck at choose account for appname. I don't know what wrong happened?
I tried a few options:
Since the app package name should not contain .example while uploading apk to google play store, I did the same in my https://console.developers.google.com/ credentials. This didn't work.
Tried to re-install the application, but it didn't work either.
the google drive code is:
private void saveFileToDrive() {
// Start by creating a new contents, and setting a callback.
//Log.i(TAG, "Creating new contents.");
Drive.DriveApi.newDriveContents(mGoogleApiClient)
.setResultCallback(new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(DriveContentsResult result) {
// If the operation was not successful, we cannot do anything
// and must
// fail.
if (!result.getStatus().isSuccess()) {
//Log.i(TAG, "Failed to create new contents.");
return;
}
// Otherwise, we can write our data to the new contents.
//Log.i(TAG, "New contents created.");
// Get an output stream for the contents.
OutputStream outputStream = result.getDriveContents().getOutputStream();
// Write the bitmap data from it.
FileInputStream fis;
try {
fis = new FileInputStream(outputFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n;
while (-1 != (n = fis.read(buf)))
baos.write(buf, 0, n);
byte[] photoBytes = baos.toByteArray();
outputStream.write(photoBytes);
outputStream.close();
fis.close();
//Log.w(TAG, "Successfully written: ");
// let us delete the file here
File file = new File(outputFile);
if (file.delete())
{
//Log.d(TAG, "Successfully deleted.");
}
} catch (FileNotFoundException e) {
//Log.w(TAG, "FileNotFoundException: " + e.getMessage());
} catch (IOException e1) {
//Log.w(TAG, "Unable to write file contents." + e1.getMessage());
}
// Create the initial metadata - MIME type and title.
// Note that the user will be able to change the title later.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("audio/mpeg").setTitle(file_name_with_extension).build();
// Create an intent for the file chooser, and start it.
Drive.DriveApi.getRootFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, metadataChangeSet, result.getDriveContents())
;
}
});
}
/**
* Called before the activity is destroyed
*/
#Override
public void onDestroy() {
if (mAdView != null) {
mAdView.destroy();
}
super.onDestroy();
}
#Override
protected void onResume() {
super.onResume();
if (mAdView != null) {
mAdView.resume();
}
if (mGoogleApiClient == null) {
// Create the API client and bind it to an instance variable.
// We use this instance as the callback for connection and connection
// failures.
// Since no account name is passed, the user is prompted to choose.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
// Connect the client. Once connected, the camera is launched.
mGoogleApiClient.connect();
}
#Override
protected void onPause() {
if (mAdView != null) {
mAdView.pause();
}
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onPause();
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (resultCode == Activity.RESULT_OK) {
// Store the image data as a bitmap for writing later.
//Log.d(TAG, "Data is saved successfully.");
Toast.makeText(getApplicationContext(), "The file " + file_name + " has been saved successfully to your drive.", Toast.LENGTH_LONG).show();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Called whenever the API client fails to connect.
//Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
// show the localized error dialog.
GoogleApiAvailability.getInstance().getErrorDialog(this, result.getErrorCode(), 0).show();
return;
}
// The failure has a resolution. Resolve it.
// Called typically when the app is not yet authorized, and an
// authorization
// dialog is displayed to the user.
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
//Log.e(TAG, "Exception while starting resolution activity", e);
}
}
#Override
public void onConnected(Bundle connectionHint) {
//Log.i(TAG, "API client connected.");
}
#Override
public void onConnectionSuspended(int cause) {
//Log.i(TAG, "GoogleApiClient connection suspended");
}
My app is live here:
https://play.google.com/store/apps/details?id=com.khan.spyrecorder
Please help me to solve the issue.
I am trying to append data to my existing file on Google Drive using the procedure given at https://developers.google.com/drive/android/files#making_modifications
Although control is going correctly but no change is getting reflected in the file.
public void addHistoryEntry(final String location) {
final DriveFile file = Drive.DriveApi.getFile(getClient(), historyFileId);
file.open(getClient(), DriveFile.MODE_READ_WRITE, null)
.setResultCallback(new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(DriveContentsResult driveContentsResult) {
if (!driveContentsResult.getStatus().isSuccess()) {
L.c("Problem in Writing to file");
return;
}
DriveContents contents = driveContentsResult.getDriveContents();
try {
ParcelFileDescriptor parcelFileDescriptor = contents.getParcelFileDescriptor();
FileInputStream fileInputStream = new FileInputStream(parcelFileDescriptor.getFileDescriptor());
// Read to the end of the file.
fileInputStream.read(new byte[fileInputStream.available()]);
// Append to the file.
FileOutputStream fileOutputStream = new FileOutputStream(parcelFileDescriptor
.getFileDescriptor());
Writer writer = new OutputStreamWriter(fileOutputStream);
writer.write(location+"\n");
} catch (IOException e) {
e.printStackTrace();
}
contents.commit(getClient(), null).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if(status.isSuccess()) L.c("Write to file successful");
else L.c("Write to file failed");
}
});
}
});
}
Please help me to debug the code.
Can you try flushing your OutputStream, i.e. writer.flush(), before committing the contents?
Also, instead of reading over all the existing bytes in the InputStream, consider using FileOutputStream#getChannel since FileChannel has facilities to quickly seek to the end of file, i.e. fileChannel.position(fileChannel.size())
I've managed to create a backup of my database on an SD card and restore from there but realized that the purpose of my backup is to ensure the safety of the data and in this case if the physical device itself is damaged, lost, or spontaneously combusts so will the backup on the SD card. So having the backup in the same place as the original in this case, quite frankly defeats the purpose of having a backup.
So I thought of using Google Drive as a safer place to keep the db file, that and it's free. I've taken a peek into Google's quickstart demo which I got working just fine. But I still have no idea how to get this done for my case.
I've found some code to fiddle with but it's still using some deprecated methods and so far I've only managed to run it when omitting the deprecated area but it only creates a blank binary file in my Google Drive so I think that deprecated area is where it actually uploads the DB backup content. If anyone could help out that would be greatly appreciated.
I'll leave it down below in case anyone can use it to explain things to me better. I've also marked the deprecated method below, it's near the end.
public class ExpectoPatronum extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {
private static final String TAG = "MainActivity";
private GoogleApiClient api;
private boolean mResolvingError = false;
private DriveFile mfile;
private static final int DIALOG_ERROR_CODE =100;
private static final String DATABASE_NAME = "demodb";
private static final String GOOGLE_DRIVE_FILE_NAME = "sqlite_db_backup";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the Drive API instance
api = new GoogleApiClient.Builder(this).addApi(Drive.API).addScope(Drive.SCOPE_FILE).
addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();
}
#Override
public void onStart() {
super.onStart();
if(!mResolvingError) {
api.connect(); // Connect the client to Google Drive
}
}
#Override
public void onStop() {
super.onStop();
api.disconnect(); // Disconnect the client from Google Drive
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.v(TAG, "Connection failed");
if(mResolvingError) { // If already in resolution state, just return.
return;
} else if(result.hasResolution()) { // Error can be resolved by starting an intent with user interaction
mResolvingError = true;
try {
result.startResolutionForResult(this, DIALOG_ERROR_CODE);
} catch (SendIntentException e) {
e.printStackTrace();
}
} else { // Error cannot be resolved. Display Error Dialog stating the reason if possible.
ErrorDialogFragment fragment = new ErrorDialogFragment();
Bundle args = new Bundle();
args.putInt("error", result.getErrorCode());
fragment.setArguments(args);
fragment.show(getFragmentManager(), "errordialog");
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == DIALOG_ERROR_CODE) {
mResolvingError = false;
if(resultCode == RESULT_OK) { // Error was resolved, now connect to the client if not done so.
if(!api.isConnecting() && !api.isConnected()) {
api.connect();
}
}
}
}
#Override
public void onConnected(Bundle connectionHint) {
Log.v(TAG, "Connected successfully");
/* Connection to Google Drive established. Now request for Contents instance, which can be used to provide file contents.
The callback is registered for the same. */
Drive.DriveApi.newDriveContents(api).setResultCallback(contentsCallback);
}
final private ResultCallback<DriveApi.DriveContentsResult> contentsCallback = new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to create new file contents");
return;
}
String mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("db");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(GOOGLE_DRIVE_FILE_NAME) // Google Drive File name
.setMimeType(mimeType)
.setStarred(true).build();
// create a file on root folder
Drive.DriveApi.getRootFolder(api)
.createFile(api, changeSet, result.getDriveContents())
.setResultCallback(fileCallback);
}
};
final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
#Override
public void onResult(DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to create the file");
return;
}
mfile = result.getDriveFile();
mfile.open(api, DriveFile.MODE_WRITE_ONLY, null).setResultCallback(contentsOpenedCallback);
}
};
final private ResultCallback<DriveApi.DriveContentsResult> contentsOpenedCallback = new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Error opening file");
return;
}
try {
FileInputStream is = new FileInputStream(getDbPath());
BufferedInputStream in = new BufferedInputStream(is);
byte[] buffer = new byte[8 * 1024];
DriveContents content = result.getDriveContents();
BufferedOutputStream out = new BufferedOutputStream(content.getOutputStream());
int n = 0;
while( ( n = in.read(buffer) ) > 0 ) {
out.write(buffer, 0, n);
}
in.close();
commitAndCloseContents is DEPRECATED -->/**mfile.commitAndCloseContents(api, content).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status result) {
// Handle the response status
}
});**/
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
private File getDbPath() {
return this.getDatabasePath(DATABASE_NAME);
}
#Override
public void onConnectionSuspended(int cause) {
// TODO Auto-generated method stub
Log.v(TAG, "Connection suspended");
}
public void onDialogDismissed() {
mResolvingError = false;
}
public static class ErrorDialogFragment extends DialogFragment {
public ErrorDialogFragment() {}
public Dialog onCreateDialog(Bundle savedInstanceState) {
int errorCode = this.getArguments().getInt("error");
return GooglePlayServicesUtil.getErrorDialog(errorCode, this.getActivity(), DIALOG_ERROR_CODE);
}
public void onDismiss(DialogInterface dialog) {
((ExpectoPatronum) getActivity()).onDialogDismissed();
}
}
}
Both APIs used to access Google Drive deal with a binary content. So the only thing you have to do is to upload your binary DB file, give it a proper MIME type and a NAME (title).
The selection of API depends on you, GDAA behaves like a 'local' entity with uploads / downloads handled by Google Play Services, REST Api is more low-level, giving you more control, but you have to take care of networking issues (wifi on/off, etc), i.e. you usually have to build a sync service to do so. With GDAA it is done for you by GooPlaySvcs. But I digress.
I can point you to this GitHub demo, fairly recent (GooPlaySvcs 7.00.+), I use to test different REST / GDAA issues.
The MainActivity is a bit complicated by the fact that it allows for switching between different Google accounts, but if you get through these hurdles, you can use either REST or GDAA CRUD wrappers.
Take look at this line. The byte[] buffer contains binary JPEG data and it goes with "image/jpeg" mime type (and a time-based name). The only thing you have to do if is load your DB file into a byte[] buffer using a construct like this:
private static final int BUF_SZ = 4096;
static byte[] file2Bytes(File file) {
if (file != null) try {
return is2Bytes(new FileInputStream(file));
} catch (Exception ignore) {}
return null;
}
static byte[] is2Bytes(InputStream is) {
byte[] buf = null;
BufferedInputStream bufIS = null;
if (is != null) try {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
bufIS = new BufferedInputStream(is);
buf = new byte[BUF_SZ];
int cnt;
while ((cnt = bufIS.read(buf)) >= 0) {
byteBuffer.write(buf, 0, cnt);
}
buf = byteBuffer.size() > 0 ? byteBuffer.toByteArray() : null;
} catch (Exception e) {le(e);}
finally {
try {
if (bufIS != null) bufIS.close();
} catch (Exception e) {le(e);}
}
return buf;
}
I don't remember the MIME type for SQLite DB now, but I am sure it can be done since I was doing exactly that once (the code is gone now, unfortunately). And I remember I could actually access and modify the SQLite DB 'up in the cloud' using some web app.
Good Luck
UPDATE:
After I wrote the rant above I looked at the demo you're talking about. If you have it working, the easiest way is actually to plug your DB file right here, set the correct MIME and you're good to go. Take you pick.
And to address your 'deprecated' issue. GDAA is still being developed and the quickstart is over a year old. That's the world we live in :-)
You need to replace the deprecated code with:
contents.commit(api, null);
See https://developer.android.com/reference/com/google/android/gms/drive/DriveContents.html
I am trying to send an image file using smack and openfire xmpp. For this I am using FileTransferManager class. To use FileTransferManager class I am using asmack-android-6.jar. I followed this link to do file sharing. This issue is also shared in comments below on this tutorial but no good resolution is given to this issue. Then I searched over stack overflow, Many Developers have asked this question but only 1-2 have got replies that they have accepted, others not.
I studied all the answers that I found, tried all the ways that google gave me but still unable to solve this problem.
The code I used is:
d.findViewById(R.id.btnsendphoto).setOnClickListener(
new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (!filepath.equals("")) {
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(connection);
if (sdm == null) {
sdm = new ServiceDiscoveryManager(
connection);
Log.e("service discovery", "SDM");
sdm.addFeature("http://jabber.org/protocol/disco#info");
sdm.addFeature("jabber:iq:privacy");
}
mFileTransferManager = new FileTransferManager(
connection);
/*
* OutgoingFileTransfer transfer =
* mFileTransferManager
* .createOutgoingFileTransfer
* ("98c6d889473a6fae#pc/Smack");
*/
String to = connection.getRoster()
.getPresence("98c6d889473a6fae#pc")
.getFrom();
OutgoingFileTransfer transfer = mFileTransferManager
.createOutgoingFileTransfer(to);
File file = new File(filepath);
try {
//[configureProviderManager](http://paste.ubuntu.com/9932239/)
configureProviderManager(connection);
transfer.sendFile(file, "test_file");
} catch (XMPPException e) {
e.printStackTrace();
}
while(!transfer.isDone()) {
Log.d("status", transfer.getStatus().toString());
Log.d("percent", new Long(transfer.getBytesSent()).toString());
if (transfer.getStatus() == Status.error) {
Log.e("percent", "Error " + new Long(transfer.getBytesSent()).toString() + " " + transfer.getError() + " " + transfer.getException());
transfer.cancel();
}
if(transfer.getStatus().equals(Status.refused))
System.out.println("refused " + transfer.getError());
else if( transfer.getStatus().equals(Status.error))
System.out.println(" error " + transfer.getError());
else if(transfer.getStatus().equals(Status.cancelled))
System.out.println(" cancelled " + transfer.getError());
else
System.out.println("Success");
}
}
d.dismiss();
}
});
The logcat I got is very big, so I gave link of that. So can anyone tell what mistake I am making or can suggest what amendment I make to achieve task
This problem got solved using this link answer don't know why its downvoted. Lemme share answer here also
d.findViewById(R.id.btnsendphoto).setOnClickListener(
new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (!filepath.equals("")) {
ServiceDiscoveryManager sdm = ServiceDiscoveryManager
.getInstanceFor(connection);
if (sdm == null) {
sdm = new ServiceDiscoveryManager(
connection);
Log.e("service discovery", "SDM");
sdm.addFeature("http://jabber.org/protocol/disco#info");
sdm.addFeature("jabber:iq:privacy");
}
configureProviderManager(connection);
FileTransferNegotiator.IBB_ONLY = true;
FileTransferNegotiator.setServiceEnabled(connection, true);
mFileTransferManager = new FileTransferManager(
connection);
/*
* OutgoingFileTransfer transfer =
* mFileTransferManager
* .createOutgoingFileTransfer
* ("98c6d889473a6fae#pc/Smack");
*/
String to = connection.getRoster()
.getPresence("98c6d889473a6fae#pc")
.getFrom();
final OutgoingFileTransfer transfer = mFileTransferManager
.createOutgoingFileTransfer(to);
File file = new File(filepath);
try {
configureProviderManager(connection);
transfer.sendFile(file, "test_file");
} catch (XMPPException e) {
e.printStackTrace();
}
new AsyncTask<Void, Void, Void>() {
protected void onPreExecute() {
}
#Override
protected Void doInBackground(Void... params) {
while (!transfer.isDone()) {
if (transfer.getStatus().equals("Error")) {
Log.d("file transfer",
"ERROR!!! " + transfer.getError());
} else if (transfer.getStatus().equals("Cancelled")
|| transfer.getStatus().equals("Refused")) {
Log.d("file transfer",
"Cancelled!!! " + transfer.getError());
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
};
protected void onPostExecute(Void result) {
if (transfer.getStatus().equals("Refused")
|| transfer.getStatus().equals("Error")
|| transfer.getStatus().equals("Cancelled")) {
Log.i("file transfer", "refused cancelled error "
+ transfer.getError());
} else {
Log.i("file transfer", "Success: " + transfer.getFileName());
}
};
}.execute();
}
d.dismiss();
}
});
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but it can be configured also the another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" part to this jid and it become this:
user1#mydomain/Resource
But /Resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
transfer.sendFile(new File("DirectoryPath"), "Description");
And that is how i have solved my problem with file transfer on smack and Openfire.
In your case form jid like this:
String to = connection.getRoster().getPresence("98c6d889473a6fae#pc").getFrom();
String Resource = connection.getRoster().getPresence("98c6d889473a6fae#pc").getFrom().getResourceOrEmpty().toString();
OutgoingFileTransfer transfer = mFileTransferManager.createOutgoingFileTransfer(to + "/" + resource);
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port - 7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
Also this can be configured the easy way, just set the resource on
XMPPTCPConnectionConfiguration.Builder .
like
XMPPTCPConnectionConfiguration.Builder configurationBuilder =
XMPPTCPConnectionConfiguration.builder();
configurationBuilder.setResource("yourResourceName");
Using the following code, which is take from android-quickstart, this code can produce multiple files with same name if you take multiple pictures. How can it be modified to ensure the file with the same name is replaced?
public class MainActivity extends Activity implements ConnectionCallbacks,
OnConnectionFailedListener {
private static final String TAG = "android-drive-quickstart";
private static final int REQUEST_CODE_CAPTURE_IMAGE = 1;
private static final int REQUEST_CODE_CREATOR = 2;
private static final int REQUEST_CODE_RESOLUTION = 3;
private GoogleApiClient mGoogleApiClient;
private Bitmap mBitmapToSave;
/**
* Create a new file and save it to Drive.
*/
private void saveFileToDrive() {
// Start by creating a new contents, and setting a callback.
Log.i(TAG, "Creating new contents.");
final Bitmap image = mBitmapToSave;
Drive.DriveApi.newContents(mGoogleApiClient).setResultCallback(new ResultCallback<ContentsResult>() {
#Override
public void onResult(ContentsResult result) {
// If the operation was not successful, we cannot do anything
// and must
// fail.
if (!result.getStatus().isSuccess()) {
Log.i(TAG, "Failed to create new contents.");
return;
}
// Otherwise, we can write our data to the new contents.
Log.i(TAG, "New contents created.");
// Get an output stream for the contents.
OutputStream outputStream = result.getContents().getOutputStream();
// Write the bitmap data from it.
ByteArrayOutputStream bitmapStream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 100, bitmapStream);
try {
outputStream.write(bitmapStream.toByteArray());
} catch (IOException e1) {
Log.i(TAG, "Unable to write file contents.");
}
// Create the initial metadata - MIME type and title.
// Note that the user will be able to change the title later.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setMimeType("image/jpeg")
.setTitle("Android Photo.png")
.build();
// Create an intent for the file chooser, and start it.
IntentSender intentSender = Drive.DriveApi
.newCreateFileActivityBuilder()
.setInitialMetadata(metadataChangeSet)
.setInitialContents(result.getContents())
.build(mGoogleApiClient);
try {
startIntentSenderForResult(
intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
} catch (SendIntentException e) {
Log.i(TAG, "Failed to launch file chooser.");
}
}
});
}
#Override
protected void onResume() {
super.onResume();
if (mGoogleApiClient == null) {
// Create the API client and bind it to an instance variable.
// We use this instance as the callback for connection and connection
// failures.
// Since no account name is passed, the user is prompted to choose.
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
// Connect the client. Once connected, the camera is launched.
mGoogleApiClient.connect();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
protected void onPause() {
if (mGoogleApiClient != null) {
mGoogleApiClient.disconnect();
}
super.onPause();
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
switch (requestCode) {
case REQUEST_CODE_CAPTURE_IMAGE:
// Called after a photo has been taken.
if (resultCode == Activity.RESULT_OK) {
// Store the image data as a bitmap for writing later.
mBitmapToSave = (Bitmap) data.getExtras().get("data");
}
break;
case REQUEST_CODE_CREATOR:
// Called after a file is saved to Drive.
if (resultCode == RESULT_OK) {
Log.i(TAG, "Image successfully saved.");
mBitmapToSave = null;
// Just start the camera again for another photo.
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
REQUEST_CODE_CAPTURE_IMAGE);
}
break;
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Called whenever the API client fails to connect.
Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
if (!result.hasResolution()) {
// show the localized error dialog.
GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
return;
}
// The failure has a resolution. Resolve it.
// Called typically when the app is not yet authorized, and an
// authorization
// dialog is displayed to the user.
try {
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
} catch (SendIntentException e) {
Log.e(TAG, "Exception while starting resolution activity", e);
}
}
#Override
public void onConnected(Bundle connectionHint) {
Log.i(TAG, "API client connected.");
if (mBitmapToSave == null) {
// This activity has no UI of its own. Just start the camera.
startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
REQUEST_CODE_CAPTURE_IMAGE);
return;
}
saveFileToDrive();
}
#Override
public void onConnectionSuspended(int cause) {
Log.i(TAG, "GoogleApiClient connection suspended");
}
}
We can use the Google Drive API to:
- Find the existing file
- Get its ID
- Delete the file
- Write a new file
I open the drive file for writing as follows:
writeDriveFile(DriveFile file, Handler handler){
//see query task below to get a drive file by its name. Be careful you can get multiple data elements in the MetadataBuffer below if you have uploaded multiple files with the same name.
Task<DriveContents> openFileTask = myDriveResourceClient.openFile(file, DriveFile.MODE_WRITE_ONLY);
Then with that task write some object bytes to the stream someObject.getBytes(), no magic here. Then commit the results.
openFileTask
.continueWithTask(task -> {
DriveContents contents = task.getResult();
// Process contents...
try (OutputStream writer = contents.getOutputStream()) {
writer.write(someObject.getBytes());
writer.close();
}
//Add whatever metadata you want here
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setLastViewedByMeDate(new Date())
.build();
//commit the file to Google Drive
Task<Void> commitTask = myDriveResourceClient.commitContents(contents, changeSet);
handler.onWriteResults();
return commitTask;
})
.addOnFailureListener(e -> {
// Handle failure
Log.e(TAG, "Unable to read contents", e);
handler.onDriveError(e);
});
The handler is just an interface I defined for errors or results that I want to process after Drive API execution. You could also add an addOnCompleteListener() to process something once the writing was complete.
file is an instance of DriveFile that can be acquired by a query task. The metadata that come to you in the task code block has a getDriveId() method which has a asDrivefFile() method to get the Drive file you need above.
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.TITLE, "file name"))
.build();
Task<MetadataBuffer> queryTask = mDriveResourceClient.query(query);
Then process the MetadataBuffer
queryTask.continueWithTask(
task -> {
MetadataBuffer metadataBuffer = task.getResult() ;
//I have this loop because I wanted to know if there were other versions of the file on the drive
for(Metadata data : metadataBuffer) {
Log.d(TAG, "******************* metadataBuffer title is " + data.getTitle());
if(data.getTitle().equals("file name")){
//this is just a method I defined that encapsulates the drive writing code above.
writeDriveFile(data.getDriveId().asDriveFile(), jsonContent, handler);
}
}
return task;
})
.addOnCompleteListener(task -> {
//some complete tasks
}
})
.addOnFailureListener(e -> {
Log.e(TAG, "************************** Error searching for " + fileName, e);
handler.onDriveError(e);
});
This code was all for writing to the App folder but you just need to set the right scope when you create you resource client.
Deleting a drive file can be done as follows.
public void deleteDriveFile(DriveResource file){
Task<Void> deleteTask = mDriveResourceClient.delete(file);
deleteTask
.continueWith(task -> {
Log.d(TAG, "************* Deleted drive file: " + file.getDriveId().toInvariantString());
return task;
})
.addOnFailureListener(e -> {
//log some sort of error for yourself
});
}