I'm able to upload database to Drive using the following post.
Drive API - Download/upload sql database
But I'm not able to access it directly offline without using app.
Aim: Use the db file further in different application so I want it to be in a usable format whenever I download the content directly from google drive.
I am using MODE_WRITE_ONLY to upload the file to drive from within app
mfile.open(api, DriveFile.MODE_WRITE_ONLY, new DriveFile.DownloadProgressListener()
And mime type as this String mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("db");
My db size is 44kb when I access from external sd card on phone, however it shows 40kb when I see on drive. Please suggest what can I do to make it readable so that I can directly open it in an sqlite browser because when I open it shows "File not recognized".
Do I have to make changes in the WRITE only part or mime type for db file. Please suggest what could be the problem.
Since I've successfully tested an SQLite file upload to GooDrive, I can post a piece of code that does it:
Let's assume, there is a SQLite file on your android device:
java.io.File dbFile = Context.getDatabasePath([YOUR_DB_NAME])
Then you can call this method:
upload("temp.db", dbFile, "application/x-sqlite3")
com.google.android.gms.common.api.GoogleApiClient GAC;
///...
void upload(final String titl, final File file, final String mime) {
if (GAC != null && GAC.isConnected() && titl != null && file != null) try {
Drive.DriveApi.newDriveContents(GAC).setResultCallback(new ResultCallback<DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveContentsResult contRslt) {
if (contRslt.getStatus().isSuccess()){
DriveContents cont = contRslt.getDriveContents();
if (cont != null && file2Os(cont.getOutputStream(), file)) {
MetadataChangeSet meta = new Builder().setTitle(titl).setMimeType(mime).build();
Drive.DriveApi.getRootFolder(GAC).createFile(GAC, meta, cont).setResultCallback(
new ResultCallback<DriveFileResult>() {
#Override
public void onResult(#NonNull DriveFileResult fileRslt) {
if (fileRslt.getStatus().isSuccess()) {
// fileRslt.getDriveFile(); BINGO !!!
}
}
}
);
}
}
}
});
} catch (Exception e) { e.printStackTrace(); }
}
static boolean file2Os(OutputStream os, File file) {
boolean bOK = false;
InputStream is = null;
if (file != null && os != null) try {
byte[] buf = new byte[4096];
is = new FileInputStream(file);
int c;
while ((c = is.read(buf, 0, buf.length)) > 0)
os.write(buf, 0, c);
bOK = true;
} catch (Exception e) {e.printStackTrace();}
finally {
try {
os.flush(); os.close();
if (is != null )is.close();
} catch (Exception e) {e.printStackTrace();}
}
return bOK;
}
To create a "temp.db" SQLite file in the root of your GooDrive.
You can certainly supply a different parent folder (instead of Drive.DriveApi.getRootFolder(GAC)) if you need to place your file in a different location.
Related
I am trying to upload document from my app.
Everything working fine but when i choose file from drive.
data=Intent { act=android.intent.action.VIEW dat=content://com.google.android.apps.docs.storage.legacy/enc=ckpgt5KcEEF_JYniJQafRV_5pEnu_D5UAI1WF-Lu6h2Z_Vw4
(has extras) }}
Can any body know how to handle this file.
I had already handle all files and images only facing problem with google drive files.
I am getting this content://com.google.android.apps.docs.storage.legacy/enc=ckpgt5KcEEF_JYniJQafRV_5pEnu_D5UAI1WF-Lu6h2Z_Vw4 in intent data Uri.
Handle Uri received by Google-Drive files when selected through file chooser.
as stated earlier it receives Virtual File Uri.
I found this sample code simple and easy to understand.
the given code sample worked for me .hope it works in your case.
1.So detect this Uri is received by google drive.
public static File getFileFromUri(final Context context, final Uri uri) throws Exception {
if (isGoogleDrive(uri)) // check if file selected from google drive
{
return saveFileIntoExternalStorageByUri(context, uri);
}else
// do your other calculation for the other files and return that file
return null;
}
public static boolean isGoogleDrive(Uri uri)
{
return "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
}
2.if yes,the uri is stored to external path(here its root directory u can change it according to your need) and the file with that uri is created.
public static File saveFileIntoExternalStorageByUri(Context context, Uri uri) throws
Exception {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
int originalSize = inputStream.available();
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
String fileName = getFileName(context, uri);
File file = makeEmptyFileIntoExternalStorageWithTitle(fileName);
bis = new BufferedInputStream(inputStream);
bos = new BufferedOutputStream(new FileOutputStream(
file, false));
byte[] buf = new byte[originalSize];
bis.read(buf);
do {
bos.write(buf);
} while (bis.read(buf) != -1);
bos.flush();
bos.close();
bis.close();
return file;
}
public static String getFileName(Context context, Uri uri)
{
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
public static File makeEmptyFileIntoExternalStorageWithTitle(String title) {
String root = Environment.getExternalStorageDirectory().getAbsolutePath();
return new File(root, title);
}
Note:Here the virtual file is retrieved from Intent getData() and used in context.getContentResolver().openInputStream(intent.getData()), this will return an InputStream. It's handle to get selected file from google drive.
for more info go through this link
I think you are getting Virtual File Uri from google drive
Read more about Virtual Files
FROM DOCS
Virtual Files
Android 7.0 adds the concept of virtual files to the Storage Access Framework. The virtual files feature allows your DocumentsProvider to return document URIs that can be used with an ACTION_VIEW intent even if they don't have a direct bytecode representation. Android 7.0 also allows you to provide alternate formats for user files, virtual or otherwise
Now question is how to check the the Uri is VirtualFile or not
You can find sample code from DOCS Open virtual files
first check that Uri is VirtualFile or not
private boolean isVirtualFile(Uri uri) {
if (!DocumentsContract.isDocumentUri(this, uri)) {
return false;
}
Cursor cursor = getContentResolver().query(
uri,
new String[] { DocumentsContract.Document.COLUMN_FLAGS },
null, null, null);
int flags = 0;
if (cursor.moveToFirst()) {
flags = cursor.getInt(0);
}
cursor.close();
return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}
The following code snippet shows how to check whether a virtual file can be represented as an image, and if so, gets an input stream from the virtual file
private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
throws IOException {
ContentResolver resolver = getContentResolver();
String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);
if (openableMimeTypes == null ||
openableMimeTypes.length < 1) {
throw new FileNotFoundException();
}
return resolver
.openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
.createInputStream();
}
For more information of Virtual Files you can read below article
Virtual Files FAQ
Open files using storage access framework
An Android Storage Access Framework Example
I am trying to upload document from my app.
Everything working fine but when i choose file from drive.
data=Intent { act=android.intent.action.VIEW dat=content://com.google.android.apps.docs.storage.legacy/enc=ckpgt5KcEEF_JYniJQafRV_5pEnu_D5UAI1WF-Lu6h2Z_Vw4
(has extras) }}
Can any body know how to handle this file.
I had already handle all files and images only facing problem with google drive files.
I am getting this content://com.google.android.apps.docs.storage.legacy/enc=ckpgt5KcEEF_JYniJQafRV_5pEnu_D5UAI1WF-Lu6h2Z_Vw4 in intent data Uri.
Handle Uri received by Google-Drive files when selected through file chooser.
as stated earlier it receives Virtual File Uri.
I found this sample code simple and easy to understand.
the given code sample worked for me .hope it works in your case.
1.So detect this Uri is received by google drive.
public static File getFileFromUri(final Context context, final Uri uri) throws Exception {
if (isGoogleDrive(uri)) // check if file selected from google drive
{
return saveFileIntoExternalStorageByUri(context, uri);
}else
// do your other calculation for the other files and return that file
return null;
}
public static boolean isGoogleDrive(Uri uri)
{
return "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
}
2.if yes,the uri is stored to external path(here its root directory u can change it according to your need) and the file with that uri is created.
public static File saveFileIntoExternalStorageByUri(Context context, Uri uri) throws
Exception {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
int originalSize = inputStream.available();
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
String fileName = getFileName(context, uri);
File file = makeEmptyFileIntoExternalStorageWithTitle(fileName);
bis = new BufferedInputStream(inputStream);
bos = new BufferedOutputStream(new FileOutputStream(
file, false));
byte[] buf = new byte[originalSize];
bis.read(buf);
do {
bos.write(buf);
} while (bis.read(buf) != -1);
bos.flush();
bos.close();
bis.close();
return file;
}
public static String getFileName(Context context, Uri uri)
{
String result = null;
if (uri.getScheme().equals("content")) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} finally {
cursor.close();
}
}
if (result == null) {
result = uri.getPath();
int cut = result.lastIndexOf('/');
if (cut != -1) {
result = result.substring(cut + 1);
}
}
return result;
}
public static File makeEmptyFileIntoExternalStorageWithTitle(String title) {
String root = Environment.getExternalStorageDirectory().getAbsolutePath();
return new File(root, title);
}
Note:Here the virtual file is retrieved from Intent getData() and used in context.getContentResolver().openInputStream(intent.getData()), this will return an InputStream. It's handle to get selected file from google drive.
for more info go through this link
I think you are getting Virtual File Uri from google drive
Read more about Virtual Files
FROM DOCS
Virtual Files
Android 7.0 adds the concept of virtual files to the Storage Access Framework. The virtual files feature allows your DocumentsProvider to return document URIs that can be used with an ACTION_VIEW intent even if they don't have a direct bytecode representation. Android 7.0 also allows you to provide alternate formats for user files, virtual or otherwise
Now question is how to check the the Uri is VirtualFile or not
You can find sample code from DOCS Open virtual files
first check that Uri is VirtualFile or not
private boolean isVirtualFile(Uri uri) {
if (!DocumentsContract.isDocumentUri(this, uri)) {
return false;
}
Cursor cursor = getContentResolver().query(
uri,
new String[] { DocumentsContract.Document.COLUMN_FLAGS },
null, null, null);
int flags = 0;
if (cursor.moveToFirst()) {
flags = cursor.getInt(0);
}
cursor.close();
return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}
The following code snippet shows how to check whether a virtual file can be represented as an image, and if so, gets an input stream from the virtual file
private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
throws IOException {
ContentResolver resolver = getContentResolver();
String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);
if (openableMimeTypes == null ||
openableMimeTypes.length < 1) {
throw new FileNotFoundException();
}
return resolver
.openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
.createInputStream();
}
For more information of Virtual Files you can read below article
Virtual Files FAQ
Open files using storage access framework
An Android Storage Access Framework Example
I need to check whether a file (with unknown extension) is a valid SQLite database.
The db file is stored on the sd card. I need to import the database in my app. But if the user creates a file by the same name as of the database with any extension the file is still accepted by the code as it searches only for the name.
Is there a quick way to check for the validity of sqlite db stored on memory card.
I used this code, but it still accepts arbitrary file with same name as db.
String path = Environment.getExternalStorageDirectory().getPath() + "/FOLDER/DB_FILE";
SQLiteDatabase database;
database = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
if (database == null) {
Toast.makeText(getApplicationContext(), "Error: Incorrect/Corrupted File", Toast.LENGTH_SHORT).show();
return;
} else {Proceed with code here}
A SQLite database file contains a header which provides some useful information.
In particular, every SQlite database file has always in it's first 16 bytes the value: SQLite format 3\0000
So you can check if your file contains that value in its first 16 bytes:
public boolean isValidSQLite(String dbPath) {
File file = new File(dbPath);
if (!file.exists() || !file.canRead()) {
return false;
}
try {
FileReader fr = new FileReader(file);
char[] buffer = new char[16];
fr.read(buffer, 0, 16);
String str = String.valueOf(buffer);
fr.close();
return str.equals("SQLite format 3\u0000");
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
Thanks #Drilon Kurti.
But from my own situation I want to add something.
Sometimes, When trying to open Database, if the file even not valid, an empty database may create with the same name in the app memory, as a result many problems occur, and sometimes, that, for the second time it will direct fall through the memory, which is a database by name and even pass the check as a database file, but not the real one. So, In this situation Drilon Kurti's method also pass true. But finally the database will not open or cant find the required columns. In this case I have checked with a min size with the above answer.
But, in real life, in every situation the following code will not fit, it will fit when you know the min size of the db. In example, when we embedded a db with app, or read an external db which's real size we can determine before opening it, and check it with the size :
public boolean isValidSQLite(String dbPath, int minSizeInMb) {
File file = new File(dbPath);
if (!file.exists() || !file.canRead()) {
return false;
}
boolean isReadable = false ;
try {
FileReader fr = new FileReader(file);
char[] buffer = new char[16];
try {
fr.read(buffer, 0, 16);
} catch (IOException e) {
e.printStackTrace();
}
String str = String.valueOf(buffer);
fr.close();
isReadable = str.equals("SQLite format 3\u0000");
} catch (Exception e) {
e.printStackTrace();
}
if (file.length() > (1024 * 1024 * minSizeInMb) && isReadable) {
return true;
}else {
try {
file.delete();
} catch (Exception e) {
e.printStackTrace();
}
return false ;
}
}
And the database memory path should be:
String DB_PATH;
String DB_NAME;
String packageName = mContext.getPackageName();
DB_PATH = String.format("//data//data//%s//databases//", packageName);
DB_NAME = "Your_Database_tafheemul_quran.db";
String path = DB_PATH + DB_NAME;
I am used to opening my files in my apps using the next code:
public void openFile(#NonNull String uri) {
checkNotNull(uri);
File file = new File(uri);
String dataType = null;
if (ContentTypeUtils.isPdf(uri)) dataType = "application/pdf";
else if (ContentTypeUtils.isImage(uri)) dataType = "image/*";
if (file.exists() && dataType != null) {
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file), dataType);
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Intent intent = Intent.createChooser(target, "Open file");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "There is a problem when opening the file :(");
}
} else {
Toast.makeText(getContext(), "Invalido", Toast.LENGTH_LONG).show();
}
}
I had always used static files so this was enough, but now I am using the Google Drive SDK for Android. I possess the driveId of the file I want to open but the problem is I cannot find a clean way to open the file contents I obtain by doing this:
Drive.DriveApi.fetchDriveId(mGoogleApiClient, documentFile.getDriveId())
.setResultCallback(driveIdResult -> {
PendingResult<DriveApi.DriveContentsResult> open =
driveIdResult.getDriveId().asDriveFile().open(
mGoogleApiClient,
DriveFile.MODE_READ_ONLY,
null);
open.setResultCallback(result -> {
DriveContents contents = result.getDriveContents();
InputStream inputStream = contents.getInputStream();
// I know I can get the input stream, and use it to write a new file.
});
});
So the only thing that comes to my mind is creating a static route to create a file every time I have to open it, and erasing it every time I have to open a new file.
What I have understood up until now is that the Google Drive API for Android already saves an instance of the file so what I have in mind sounds unnecessary, I would like to know if there is a better way to achieve this. Is there a way I can open the file and do something similar to what I do with the Intent.ACTION_VIEW in a cleaner way?
Thanks in advance.
Well since it seems this will not be answered I will post what I did. All I did was create a temp file where I put my contents to be read. I still don't know if it was the best choice so this question will still be opened for a better answer.
open.setResultCallback(result -> {
DriveContents contents = result.getDriveContents();
InputStream inputStream = contents.getInputStream();
writeTempFile(inputStream);
});
And here the implementation of the `writeTempFile`:
private synchronized File writeTempFile(#NonNull InputStream inputStream) {
checkNotNull(inputStream);
File filePath = new File(mActivity.getFilesDir(), "TempFiles");
if (!filePath.exists()) filePath.mkdirs();
File file = new File(filePath, TEMP_FILE);
try {
OutputStream outputStream = new FileOutputStream(file);
IOUtils.copyLarge(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
return file;
}
In my app, I need to upload multiple files (1 sqlite db file and multiple image files) for backup purpose to user's google drive.
I am using android google drive api, but not sure, how to do back to back file uploads and then later on downloads like this.
The db file obviously comes from /data/data//databases kind of directory whereas images are stored in pictures directory. I need to grab all of these one by one and upload to drive.
Also, I have seen that if a given file (with the same title) already exists, even then, a new file with the same title is created on drive (obviously has diff DriveId but same title). I would like to check, if the file exists and only upload if it doesn't, else skip that file.
Please help.. I have been trying to refer the android demos on github by google, but have been only able to do bits and pieces using that.
File title is not unique in Drive API. However you can save the IDs of your newly created files in your app's local storage, so you can check against the IDs in the Drive side when you want to upload the file again.
You can use the CreateFileActvity.java from the Google Drive Demo GitHub page. It will return a file ID after you create a file successfully, so you can store the ID in your local storage.
Sample code from CreateFileActivity.java:
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 with content: " + result.getDriveFile().getDriveId());
}
};
Just in case somebody is looking how to upload multiple files to Drive, here is solution that worked for me:
for(String fileName: fileNameArrayList){backupImage(fileName);}
private void backupImage(String fileName) {
Drive.DriveApi.newDriveContents(mGoogleApiClient).setResultCallback(
new BackupImagesContentsCallback(mContext, mGoogleApiClient, fileName));
}
Backup callback:
public class BackupImagesContentsCallback implements ResultCallback<DriveApi.DriveContentsResult> {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult driveContentsResult) {
if (!driveContentsResult.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to backup images");
return;
}
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(mFileName) // Google Drive File name
.setMimeType("image/jpeg")
.setStarred(true).build();
Drive.DriveApi.getAppFolder(mGoogleApiClient)
.createFile(mGoogleApiClient, changeSet, driveContentsResult.getDriveContents())
.setResultCallback(backupImageFileCallback);
}
final private ResultCallback<DriveFolder.DriveFileResult> backupImageFileCallback = new ResultCallback<DriveFolder.DriveFileResult>() {
#Override
public void onResult(#NonNull DriveFolder.DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
Log.v(TAG, "Error while trying to backup images");
return;
}
DriveFile mImageFile;
mImageFile = result.getDriveFile();
mImageId = result.getDriveFile().getDriveId();
mImageFile.open(mGoogleApiClient, DriveFile.MODE_WRITE_ONLY, (bytesDownloaded, bytesExpected) -> {
}).setResultCallback(backupImagesContentsOpenedCallback);
}
};
final private ResultCallback<DriveApi.DriveContentsResult> backupImagesContentsOpenedCallback =
new ResultCallback<DriveApi.DriveContentsResult>() {
#Override
public void onResult(#NonNull DriveApi.DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
return;
}
DriveContents contents = result.getDriveContents();
BufferedOutputStream bos = new BufferedOutputStream(contents.getOutputStream());
byte[] buffer = new byte[1024];
int n;
File imageDirectory = new File(mContext.getFilesDir(),
Constants.IMAGE_DIRECTORY_NAME);
try {
FileInputStream is = new FileInputStream(new File(imageDirectory,
mFileName));
BufferedInputStream bis = new BufferedInputStream(is);
while ((n = bis.read(buffer)) > 0) {
bos.write(buffer, 0, n);
}
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
contents.commit(mGoogleApiClient, null);
}
};
}
This is not perfect solution, just a working code.