Load file from external storage using chooser - android

So, I'm trying to load a simple .txt file like this:
private void showFileChooser() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/plain");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Upload"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
}
And of course, catching the result like this:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case FILE_SELECT_CODE:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
Uri uri = data.getData();
It works great on genymotion and on my device if I use a file explorer that I have installed (File explorer, see image abovew), now, If use the chooser directly like this:
It says it cannot find the specified file. (FileNotFoundException)
Now, I've realized that the URIs I get from these two file choosers are different
content://com.android.externalstorage.documents/document/primary%3ADownload%2Ffile.txt <- THIS DOESNT WORK (android built in explorer)
content://media/external/file/44751 <- THIS WORKS (custom explorer)
Does anyone have any idea why I'm getting different URIs for the SAME file.
EDIT:
I tried to use a content resolver to get the file path from the URI like this:
public class Utils {
public static String getRealPathFromURI(Context context, Uri contentUri) {
Cursor cursor = null;
try {
String[] proj = {MediaStore.Files.FileColumns.DATA};
cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(proj[0]);
cursor.moveToFirst();
return cursor.getString(column_index);
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
Still no luck :(

#CommonsWare's asnwer must be the right way of solving this, but I'm not sure how to implement his solution.
I've ended up doing what #Paul Burke recomends on this link:
Android Gallery on KitKat returns different Uri for Intent.ACTION_GET_CONTENT
EDIT
For my particular case, this is how my code ended up. I leave this here for future reference. (I'm using #CommonsWare's explanation) see his answer above.
try {
InputStream inputStream = getContentResolver().openInputStream(uri);
BufferedReader br = null;
if (inputStream != null) {
br = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
} else {
subscriber.onError(new InputException("There's something seriously wrong with this file"));
}
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}

I tried to use a content resolver to get the file path from the URI like this
There is no file path. A Uri does not necessarily point to a file that you can access on your local filesystem, just as the URL to this Web page does not necessarily point to a file that you can access on your local filesystem. The Uri might:
represent a file held in internal storage of the other app
represent a BLOB column value in a database
represent a file held in "the cloud", to be downloaded when requested
etc.
Use a ContentResolver and methods like openInputStream() to read in the content represented by the Uri, just like you would use HttpUrlConnection to read in the content represented by the URL for this Web page. openInputStream() takes the Uri as a parameter and returns an InputStream. You would use that InputStream the same way you would any other InputStream (e.g., FileInputStream, InputStream from HttpUrlConnection). The exact mechanics of that will depend a lot on the underlying data (e.g., read in a string, pass to BitmapFactory to decode a Bitmap).

Related

File doesn't exist when selecting a file with Intent.createChooser [duplicate]

I am working on an app where I want to be able to export and import some data from the app, on a .txt file.
The minimum API of the app is 21.
The export part works well, but I am having trouble with the import part.
I open the file explorer :
butImportPatient.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
startActivityForResult(intent, IMPORTPATIENT_ACTIVITY_REQUEST_CODE);
}
});
This looks like it is working.
But my onActivityResult doesn't work, I didn't find how I can get the file from the Uri.
For now, here is my code :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IMPORTPATIENT_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
File file = new File(data.getData().getPath()) ;
String path = file.getAbsolutePath() ;
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(path));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this) ;
builder.setMessage(path)
.show() ;
}
}
It is a mix of multiple posts I saw here, but none seems to work.
I get this path :
/document/home:List.txt
It creates FileNotFoundException. How can I get the real path of the file ?
I didn't find how I can get the file from the Uri.
There is no file. ACTION_OPEN_DOCUMENT and ACTION_GET_CONTENT do not open a file. They open a document. That document might be a file. It might not.
That Uri might point to:
A local file on external storage
A local file on internal storage for the other app
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly
A stream of bytes held in a BLOB column in a database
A piece of content that needs to be downloaded by the other app first
...and so on
How can I get the real path of the file ?
You don't.
If you wish to only accept files, integrate a file chooser library instead of using ACTION_OPEN_DOCUMENT or
ACTION_GET_CONTENT. Just bear in mind that filesystem access to external storage is limited on Android 10+.
If you use ACTION_GET_CONTENT, and the scheme of the Uri that you get is file, then getPath() will be a filesystem path.
Otherwise, you need to understand that you have no idea where the document is coming from, and stop thinking in terms of "real path of the file". Use ContentResolver and openInputStream() to make a copy of the content to some file that you control, then work with that file.

READ_EXTERNAL_STORAGE permission needed while using storage access framework in android [duplicate]

I am working on an app where I want to be able to export and import some data from the app, on a .txt file.
The minimum API of the app is 21.
The export part works well, but I am having trouble with the import part.
I open the file explorer :
butImportPatient.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
startActivityForResult(intent, IMPORTPATIENT_ACTIVITY_REQUEST_CODE);
}
});
This looks like it is working.
But my onActivityResult doesn't work, I didn't find how I can get the file from the Uri.
For now, here is my code :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IMPORTPATIENT_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
File file = new File(data.getData().getPath()) ;
String path = file.getAbsolutePath() ;
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(path));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this) ;
builder.setMessage(path)
.show() ;
}
}
It is a mix of multiple posts I saw here, but none seems to work.
I get this path :
/document/home:List.txt
It creates FileNotFoundException. How can I get the real path of the file ?
I didn't find how I can get the file from the Uri.
There is no file. ACTION_OPEN_DOCUMENT and ACTION_GET_CONTENT do not open a file. They open a document. That document might be a file. It might not.
That Uri might point to:
A local file on external storage
A local file on internal storage for the other app
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly
A stream of bytes held in a BLOB column in a database
A piece of content that needs to be downloaded by the other app first
...and so on
How can I get the real path of the file ?
You don't.
If you wish to only accept files, integrate a file chooser library instead of using ACTION_OPEN_DOCUMENT or
ACTION_GET_CONTENT. Just bear in mind that filesystem access to external storage is limited on Android 10+.
If you use ACTION_GET_CONTENT, and the scheme of the Uri that you get is file, then getPath() will be a filesystem path.
Otherwise, you need to understand that you have no idea where the document is coming from, and stop thinking in terms of "real path of the file". Use ContentResolver and openInputStream() to make a copy of the content to some file that you control, then work with that file.

Android - FileNotFoundException on Xiaomi (but not on my other smartphone) [duplicate]

I am working on an app where I want to be able to export and import some data from the app, on a .txt file.
The minimum API of the app is 21.
The export part works well, but I am having trouble with the import part.
I open the file explorer :
butImportPatient.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
startActivityForResult(intent, IMPORTPATIENT_ACTIVITY_REQUEST_CODE);
}
});
This looks like it is working.
But my onActivityResult doesn't work, I didn't find how I can get the file from the Uri.
For now, here is my code :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == IMPORTPATIENT_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) {
File file = new File(data.getData().getPath()) ;
String path = file.getAbsolutePath() ;
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(path));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append("\n");
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this) ;
builder.setMessage(path)
.show() ;
}
}
It is a mix of multiple posts I saw here, but none seems to work.
I get this path :
/document/home:List.txt
It creates FileNotFoundException. How can I get the real path of the file ?
I didn't find how I can get the file from the Uri.
There is no file. ACTION_OPEN_DOCUMENT and ACTION_GET_CONTENT do not open a file. They open a document. That document might be a file. It might not.
That Uri might point to:
A local file on external storage
A local file on internal storage for the other app
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly
A stream of bytes held in a BLOB column in a database
A piece of content that needs to be downloaded by the other app first
...and so on
How can I get the real path of the file ?
You don't.
If you wish to only accept files, integrate a file chooser library instead of using ACTION_OPEN_DOCUMENT or
ACTION_GET_CONTENT. Just bear in mind that filesystem access to external storage is limited on Android 10+.
If you use ACTION_GET_CONTENT, and the scheme of the Uri that you get is file, then getPath() will be a filesystem path.
Otherwise, you need to understand that you have no idea where the document is coming from, and stop thinking in terms of "real path of the file". Use ContentResolver and openInputStream() to make a copy of the content to some file that you control, then work with that file.

Read .txt file from anywhere on the phone

In my app I have this code allowing the user to select a file :
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/plain");
startActivityForResult(intent,1);
The user can select the .txt file from anywhere in his phone, even from google drive. When the file selection is done I retrieve a Uri object corresponding to the file. The problem is I can't use this Uri to read the file because it is not valid. Here is my code :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
File file = new File(uri.toString());
try{
InputStream inputStream = new FileInputStream(file);
int content;
while ((content = inputStream.read()) != -1) {
Log.d("===>", String.valueOf((char) content));
}
}catch (Exception e){
Log.d("===>", e.toString());
}
}
}
}
I always get a fileNotFoundException. My question is, is there a way to read the selected file (without knowing in advance the location it will come from). And if not, is there a way to copy the selected file in a folder from which I would easily get it ?
The problem is I can't use this Uri to read the file because it is not valid.
That is because a Uri is not a file.
is there a way to read the selected file (without knowing in advance the location it will come from)
The user did not select a file. The user selected a piece of content.
To consume the content represented by the Uri, call openInputStream() on a ContentResolver, passing in the Uri. This gives you an InputStream that you can use to read in the content.

Correct way to choose a file for open

I am using this intent to open a chooser to pick up a file:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setDataAndType(Uri.parse(getExternalFilesDir(null).toString()), "*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
Intent chooser = Intent.createChooser(intent, "Choose File");
startActivityForResult(chooser, FILE_SELECT);
I don't fully understand it, but it works. I copied it from some example in the web
The result is an uri, but I find the processing of the uri, also copied from the example, too cumbersome
if(uri != null) {
if("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {"_data"};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
int column_index = cursor.getColumnIndexOrThrow("_data");
if(cursor.moveToFirst()) {
return cursor.getString(column_index);
}
}
catch (Exception e) {}
} else if("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
}
Is this the simplest way to let the user pick a file and get its path?
Why do I need to check if the uri is "content"? I just want to open a text or zipped file the user chose in the intent.
Can I ignore the "content" check, just assume "file" and simply use uri.getPath() ?
Edit I: Is there a way to force choosing with uri scheme "file"?
Yes. This is correct. You are basically starting an intent to get access to the native app. which in this case is the file system. The next would be a part of the onActivityResultData() which would then use the uri on the intent and get the contents of the file that has been selected.
I'm wondering if Android isn't trying to push us away from file paths and towards Uris. Try something like:
InputStream inputStream=contentResolver.openInputStream(uri);
InputStreamReader file = new InputStreamReader(inputStream);
int size = file.read(text,0,mMaxLen);

Categories

Resources