I am trying to download an image using a url through a service I've created and when I inspect my device's file directory through Android Studio debugger, I can see the file in the device:
The red file is the downloaded file in question. The green image file is a file I manually dragged in to the directory through Windows Explorer.
I can successfully see and open the green file in my Gallery app but the red file (and all the other files listed in that directory) are no where to be found. I cannot see them in Windows Explorer either. Am I supposed to identify them as images somewhere in my code so that the file system knows it's an image?
This is the part where I download the image:
Request.Builder builder = new Request.Builder();
builder = builder.url(downloadFileUrl);
builder = builder.addHeader("RANGE", "bytes=" + existLocalFileLength);
Request request = builder.build();
Call call = okHttpClient.newCall(request);
Response response = call.execute();
if (response != null && response.isSuccessful()) {
RandomAccessFile downloadFile = new RandomAccessFile(existLocalFile, "rw");
downloadFile.seek(existLocalFileLength);
ResponseBody responseBody = response.body();
InputStream inputStream = responseBody.byteStream();
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
byte data[] = new byte[102400];
long totalReadLength = 0;
int readLength = bufferedInputStream.read(data);
while (readLength != -1) {
if (getDownloadManager().isDownloadPaused()) {
ret = DOWNLOAD_PAUSED;
break;
} else if (getDownloadManager().isDownloadCanceled()) {
ret = DOWNLOAD_CANCELED;
break;
} else {
downloadFile.write(data, 0, readLength);
totalReadLength = totalReadLength + readLength;
int downloadProgress = (int)((totalReadLength + existLocalFileLength) * 100 / downloadFileLength);
getDownloadManager().updateTaskProgress(downloadProgress);
readLength = bufferedInputStream.read(data);
}
}
}
You need to scan the saved file using MediaScannerConnection:
private void scanFile(String path) {
MediaScannerConnection.scanFile(context,
new String[] { path }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.d("Tag", "Scan finished. You can view the image in the gallery now.");
}
});
}
Call this on your existLocalFile's path:
scanFile(existLocalFile.getAbsolutePath());
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 started an Intent
Intent = new Intent();
Intent.SetType("image/*");
Intent.PutExtra(Intent.ExtraAllowMultiple,true);
Intent.SetAction(Intent.ActionGetContent);
StartActivityForResult(Intent.CreateChooser(Intent, "Select Picture"), code);`
I am working with bitmap.Compress(...) function and took the uri from above intent. I want to delete that bitmap file and recreate the file with the same name (in short: Replacing the existing file).
My physical devices is Samsung J5 having Internal storage and external sd card storage like /storage/emulated/0/ and /storage/D660-18BD/ (D668-18BD is not fixed, it changes according to devices).
When I tried to create a new file in the storage/D660-18BD/newfile.jpg, it says Access to the path is denied.
I googled a lot but no success
What I had tried
1. Added Read/Write External Storage (but android take it as Internal storage) in the manifest file
2. Added the above permission at Runtime also alongwith Read/Write URI permission.
Here is a demo in the thread, download it, it is a Xamarin.Forms project,
1) Run it on your phone, then it will create this path:/storage/11E3-2116/Android/data/com.companyname.cropsample/files/Pictures
2) Add this in the MainActivity, under LoadApplication(new App());:
Java.IO.File sdCardPath = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
// filePath: /storage/11E3-2116/Android/data/com.companyname.cropsample/files/Pictures
string destPath = Path.Combine("/storage/11E3-2116/Android/data/com.companyname.cropsample/files/Pictures/", "test.png");
string originPath = Path.Combine(sdCardPath.AbsolutePath, "nULSa.png");
Android.Util.Log.Error("lv", destPath);
FileOutputStream fos = new FileOutputStream(destPath, false);
FileInputStream fis = new FileInputStream(originPath);
int b;
byte[] d = new byte[1024 * 1024];
while ((b = fis.Read(d)) != -1)
{
fos.Write(d, 0, b);
}
fos.Flush();
fos.Close();
fis.Close();
storage/D660-18BD/ is wrong path, you can't get the permission, you have the permission only in your package folder. So you need create the path. Sorry I can't find where the path created, maybe you can find it.
This is the result:
The only thing you need to is to create this path: /storage/11E3-2116/Android/data/com.companyname.cropsample/files/Pictures firstly, once it created, then you can write. I hope it will help.
Update:
I find the solution:
using Android.App;
using Android.Widget;
using Android.OS;
using System.IO;
using Java.IO;
using System.Collections.Generic;
using Android.Content;
using Android.OS.Storage;
using Java.Lang.Reflect;
using System;
namespace WritToSd
{
[Activity(Label = "WritToSd", MainLauncher = true)]
public class MainActivity : Activity
{
string s;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// important codes start:
List<string> avaliableStorages = getAvaliableStorages(this);
for (int i=0;i<avaliableStorages.Count;i++) {
// because there is only one external sd card, so s is the path we need.
s = avaliableStorages[i];
}
var str=this.GetExternalFilesDir(null).AbsolutePath;
string direction = s + "/Android/data/" + this.PackageName + "/files/Pictures";
Java.IO.File file = new Java.IO.File(direction);
if (!file.Exists())
{
file.Mkdirs();
}
// end
Java.IO.File sdCardPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
// filePath: /storage/11E3-2116/Android/data/com.companyname.cropsample/files/Pictures
string destPath = Path.Combine(direction, "test.png");
string originPath = Path.Combine(sdCardPath.AbsolutePath, "test.png");
Android.Util.Log.Error("lv", destPath);
FileOutputStream fos = new FileOutputStream(destPath, false);
FileInputStream fis = new FileInputStream(originPath);
int b;
byte[] d = new byte[1024 * 1024];
while ((b = fis.Read(d)) != -1)
{
fos.Write(d, 0, b);
}
fos.Flush();
fos.Close();
fis.Close();
}
public List<string> getAvaliableStorages(Context context)
{
List<string> list = null;
try
{
var storageManager = (Android.OS.Storage.StorageManager)context.GetSystemService(Context.StorageService);
var volumeList = (Java.Lang.Object[])storageManager.Class.GetDeclaredMethod("getVolumeList").Invoke(storageManager);
list = new List<string>();
foreach (var storage in volumeList)
{
Java.IO.File info = (Java.IO.File)storage.Class.GetDeclaredMethod("getPathFile").Invoke(storage);
if ((bool)storage.Class.GetDeclaredMethod("isEmulated").Invoke(storage) == false && info.TotalSpace > 0)
{
list.Add(info.Path);
}
}
}
catch (Exception e)
{
}
return list;
}
}
}
}
I need to upload files using multipart form.
I can upload images from path below:
/storage/emulated/0/Pictures/Screenshots/Screenshot_20160614-224624.png
However, when I try to upload a PDF file, it fails. PDF file path is :
/storage/emulated/0/Android/data/com.google.android.apps.docs/cache/projector/pdf.pdf
Can any one tell me why it is work with image and fail with pdf ??
This is my code :
try {
MultipartUtility multipart = new MultipartUtility(ForSaleConstants.EXTRA_FILE_UPLOADER, ForSaleNetworkManager.CHARACTER_SET);
multipart.addFormField(ForSaleConstants.DEVICE_ID, deviceId);
String uriString = extraFileUri.toString();
Log.e("File_URI", uriString);
uriString = Uri.decode(uriString);
if (uriString.contains("file://")) {
uriString = uriString.replace("file://", "");
File uploadFile = new File(uriString);
multipart.addFilePart(("file"), uploadFile);
} else if (uriString.contains("content://")) {
uriString = FileManager.getInstance().getRealPathFromImageUri(context, Uri.parse(uriString));
File uploadFile = new File(uriString);
multipart.addFilePart(("file"), uploadFile);
} else {
try {
File uploadFile = new File(uriString);
multipart.addFilePart(("file"), uploadFile);
}catch (Exception e) {
}
}
List<String> response = multipart.finish();
if(response != null && response.size() > 0) {
mStopTime = System.nanoTime();
PerformanceManager.getInstance().showCalculatedTime("uploadExtraFile [doInBackground]", mStartTime, mStopTime);
JSONObject json = ForSaleNetworkManager.convertStringToJSONObject(response.get(0));
BaseResponse response1 = new BaseResponse(json, null);
return response1;
}
} catch (Exception e) {
e.printStackTrace();
uiListener.onUploadExtraFileCompleted(null, AppError.DATA_ERROR);
return null;
}
Is this your application package ?
com.google.android.apps.docs
/storage/emulated/0/Android/data/com.google.android.apps.docs/cache/projector/pdf.pdf
If not you cannot access it because of sandboxing.
Update
Each android application run in its own linux process hence you cannot access files in different application unless its stored in the common location. Like your image is. Hence you are not able to upload the pdf file
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.