Read .txt file from anywhere on the phone - android

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.

Related

I Use Storage Access Framework to browse a file link f.cfg, when you select the file, the app need to load another file like f.dat automatically, how?

In Android Studio:
This app must use COMTRADE files. The COMTRADE files are *.cfg *.dat and *.rio files. For example, (user.cfg, user.dat and user.rio). The main file is *.cfg file, so the user must browse by SAF to find *.cfg files, when the user select this file, the app must load other files ( *.dat and *.rio) automatically.
I can not use <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> for android level 30
So I have to use Storage Access Framework, By below code the user browse *.cfg files and select a file like user.cfg
if(SDK_INT >= Build.VERSION_CODES.R)
{
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
Act.startActivityForResult(intent, 111);
}
else
{...}
Then SAF return URI of that user.cfg file, and the app does not have any URI of other files to load.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 111)
{
if (resultCode == Activity.RESULT_OK)
{
if (data != null)
{
try
{
Uri uri = null;
uri = data.getData();
InputStreamReader inputStreamReader = new InputStreamReader(getContentResolver().openInputStream(uri));
BufferedReader br = new BufferedReader(inputStreamReader);
// I want URI of user.dat how?
InputStreamReader inputStreamReader1 = new InputStreamReader(getContentResolver().openInputStream(uri1));
BufferedReader br1 = new BufferedReader(inputStreamReader1);
....
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
}
}
Please help me how can I do this? Thanks.
Please help me how can I do this?
You cannot do this, at least by means of using ACTION_OPEN_DOCUMENT, unless you ask the user to open each of your three documents in turn. Just because the user selected a .cfg file does not mean that you have any rights to any other files, including those adjacent to it.
You could try using ACTION_OPEN_DOCUMENT_TREE and let the user choose the document tree that contains your desired files. From there, you can:
Use DocumentFile.fromTreeUri() to get a DocumentFile representing the tree
Call listFiles() on that DocumentFile to get the direct contents of that tree, in the form of a list of DocumentFile objects
Call getName() on each of those to get their display names, then see which names have your desired file extensions and matching base names (foo.cfg and foo.rio and foo.dat)
For those that you want to use, call getUri() on the DocumentFile to get a Uri to use with ContentResolver and openInputStream() to read in the content

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.

Cannot create a file using the Uri of `ACTION_GET_CONTENT`

I'm trying to select pdf file from the device and upload them to the server. ACTION_GET_CONTENT is used to select pdf from the device.
sel_book.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select PDF"), 1);
}
});
On activity result I get the Uri and save it as a String.
public void onActivityResult(int requestCode, int resultCode, Intent result) {
if (resultCode == RESULT_OK) {
if (requestCode == 1) {
Uri uri = result.getData();
String uriString = uri.toString();
File myFile = new File(uriString);
path = myFile.getAbsolutePath();
}
}
}
It results the path as, /document/primary:Download/Aptitude_2016_17.pdf. I need to use this to create a new file. File selectedFile = new File(selectedFilePath);. But it doesn't create File. selectedFile.isFile() returns false. I have no idea why is it. Please help me to solve this. Thanks in advance.
ACTION_GET_CONTENT is used to select pdf from the device.
This allows the user to select a piece of content. It does not have to be a file.
On activity result I get the Uri and save it as a String.
That is not how you use a Uri.
It results the path as, /document/primary:Download/Aptitude_2016_17.pdf.
That is not a filesystem path. That is a part of a Uri that has a content scheme. You do not get a file from ACTION_GET_CONTENT. You get a Uri that points to a piece of content. That Uri could point to anything that the user and the other app choose:
A file that you can access, via a Uri with a file scheme
A file, but one that you cannot access (e.g., on internal storage of another app)
The contents of a BLOB column in the database
A piece of content that needs to be downloaded
And so on
Use ContentResovler and openInputStream() to get a stream on whatever the content is. Either use that directly (with whatever you are using to upload this content), or use that stream to make your own file with a copy of that content, so that you have a file that you can use.

Load file from external storage using chooser

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).

How to open file save dialog in android?

I have a web service which give me a byte[] array according to image id . I want to convert these byte[] to file and store a file on android where user want like save file dialog box with file same format exactly it has.
Since this is the top result in google when you search for that topic and it confused me a lot when I researched it, I thought I add an update to this question.
Since Android 19 there IS a built in save dialog. You dont event need any permission to do it (not even WRITE_EXTERNAL_STORAGE).
The way it works is pretty simple:
//send an ACTION_CREATE_DOCUMENT intent to the system. It will open a dialog where the user can choose a location and a filename
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("YOUR FILETYPE"); //not needed, but maybe usefull
intent.putExtra(Intent.EXTRA_TITLE, "YOUR FILENAME"); //not needed, but maybe usefull
startActivityForResult(intent, SOME_INTEGER);
...
//after the user has selected a location you get an uri where you can write your data to:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == SOME_INTEGER && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
//just as an example, I am writing a String to the Uri I received from the user:
try {
OutputStream output = getContext().getContentResolver().openOutputStream(uri);
output.write(SOME_CONTENT.getBytes());
output.flush();
output.close();
}
catch(IOException e) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
}
}
}
More here:
https://developer.android.com/guide/topics/providers/document-provider
The Android SDK does not provide its own file dialog, therefore you have to build your own.
You cant create a save file dialog but you can save files from ur application to android sd card with the help of below links
http://android-er.blogspot.com/2010/07/save-file-to-sd-card.html
http://www.blackmoonit.com/android/filebrowser/intents#intent.pick_file.new
First, you should create a dialog intent for saving the file, After selection by the user, you can write on that directory and specified the file without any read/write permissions. ( Since Android 19 )
Source:https://developer.android.com/training/data-storage/shared/documents-files#create-file
// Request code for creating a PDF document.
private final int SAVE_DOCUMENT_REQUEST_CODE = 0x445;
private File targetFile;
private void createFile() {
Uri reportFileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", targetFile);
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, targetFile.getName());
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document.
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
startActivityForResult(intent, SAVE_DOCUMENT_REQUEST_CODE );
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SAVE_DOCUMENT_REQUEST_CODE && resultCode == RESULT_OK){
Uri uri = data.getData();
saveFile(uri);
}
}
private void saveFile(Uri uri) {
try {
OutputStream output = getContentResolver().openOutputStream(uri);
FileInputStream fileInputStream = new FileInputStream(targetFile);
byte[] bytes = new byte[(int) targetFile.length()];
fileInputStream.read(bytes, 0, bytes.length);
output.write(bytes);
output.flush();
output.close();
Log.i(TAG, "done");
} catch (IOException e) {
Log.e(TAG, "onActivityResult: ", e);
}
}
#JodliDev already provided the accepted answer, however, startActivityForResult is now deprecated, so I want to provide my solution here using registerForActivityResult(ActivityResultContracts.CreateDocument())
First register a ActivityResultLauncher where you define what should happen with the result. We'll get the uri back that we can use for our OutpuStream. But make sure to initialize it at the beginning, otherwise you will get:
Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).
private var ics: String? = null
private val getFileUriForSavingICS = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
if(ics.isNullOrEmpty())
return#registerForActivityResult
try {
val output: OutputStream? =
context?.contentResolver?.openOutputStream(uri)
output?.write(ics?.toByteArray())
output?.flush()
output?.close()
} catch (e: IOException) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show()
}
}
Then just call your ActivityResultLauncher with .launch(...) wherever it is needed.
getFileUriForSavingICS.launch("filename.txt")
And that's about it ;-)
You can also have a closer look at ActivityResultContracts.CreateDocument(). This method provides the document saving dialog, but there are other helpful functions inside (like for starting a camera intent). Check out:
https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts
for the possible ActivityResultContracts
Or https://developer.android.com/training/basics/intents/result for some more training material and also some information how a custom contract could be created!

Categories

Resources