Android open file input stream from uri - java

I'm trying to upload a file by copying it from mobile app to server app running on windows.
I used a file chooser to let the user select the file:
public void openFile(View view) {
Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select file"), LEARN_TREE);
}
Then i got the uri from the intent of onActivityResult.
First question here is why it displays a file named "servo.dat" as numbers (in this case it shows "5889")?
After that I put the uri as an extra into another intent and use that intent to start another activity.
In the second activity I retrieve the uri.
Now I'd like to use FileInputStream to read bytes from my file in order to write them to the ObjectOutputStream created from Socket.getOutputStream().
Here is where it doesn't work. Basically the path provided here
FileInputStream fis = new FileInputStream(uri.getPath());
is incorrect. If I check on my device the file location is Download/servo.dat, the Uri in the app shows Download/5889 and the absolute path that I tried retrieving using a UriUtils library found online shows storage/emulated/0/Download/servo.dat but this one doesn't actually exist on my phone.
I think it's not so hard but I'm getting confused since I'm new to both Android app development and Android itself, please help!
I'm open to any good solution, I saw online there is the ContentResolver class that should be helpful but I didn't manage to understand how to use it :|

First question here is why it displays a file named "servo.dat" as numbers (in this case it shows "5889")?
Because it is not a file. It is a piece of content, and you are attempting to treat the path portion of a Uri as a filesystem path, which is is not.
If you want a display name for the content:
Call DocumentFile.fromSingleUri(), passing in your Uri, to create a DocumentFile
Call getDisplayName on the DocumentFile
Basically the path provided here is incorrect
That is because you are trying to treat the path portion of a Uri as a filesystem path, which it is not.
To get an InputStream, call openInputStream() on a ContentResolver, passing in your Uri. See the documentation. So, for example, from a method in an Activity, you would use InputStream inputStream = getContentResolver().openInputStream(uri);.
I'm getting confused since I'm new to both Android app development and Android itself
You may wish to consider reading a book on Android app development or taking a course in Android app development.

Related

Prevent extension prefix (1) when saving a file on Android

I'm using the following code to let users save a file on Android:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
startActivityForResult(intent, 2);
where fileName is something like "Drawing 1.ink". The problem is when a file with the same name already exists, a user is suggested to save a new file under "Drawing 1.ink (1)" name. People often save files with "ink (1)" extension.
How to prevent this and make a default suggested file name like "Drawing 1 (1).ink"? Or forcing a suggested name without "(1)"?
How to prevent this
You can't.
Or forcing a suggested name without "(1)"?
You can't.
I want to have control over file extension
You can't.
This is a system-supplied UI, not significantly different from platform-supplied "file save-as" dialogs that we have used for decades. You're welcome to file feature requests to improve the available options here, though such changes would only take effect with Android 12 or some other future version.

Filter by mimetype or extention in native Android file picker

In Android, one can use the ACTION_OPEN_DOCUMENT Intent to open the native file picker and select for example an .mp4 file. This is achived by setting the mime type to video/mp4 using the following code:
public static void pickFile(Context mContext, int REQUEST_CODE) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("video/mp4");
((Activity) mContext).startActivityForResult(intent, REQUEST_CODE);
}
In my project, I want to pick a custom extension file whose mime type is not known in android's MimeTypeMap like for example .qgs or .dcm files.
To solve this I see two possibilities that we, so far, failed to implement:
filter by extension in the ACTION_OPEN_DOCUMENT Intent
register a new mime type to android so that it can be used with the ACTION_OPEN_DOCUMENT Intent
is either of those options doable and how?
or are there other approaches I missed without coding my own file picker?
You can try creating a custom file picker and pass the mount storage points and recursively iterate on all the file types:
public void scanFiles(File file) {
File[] fileArray = file.listFiles();
for (File f : fileArray){
if (f.isDirectory())
scanFiles(f);
if (f.isFile() && (f.getpath().endswith("..qgs") || //any extensions)) {
//Add to your list
}
}
}
And call scan files for all the mount points in android using a process builder and mount command and you can add checks for all the mount points you wish to support.
one can use the ACTION_OPEN_DOCUMENT Intent to open the native file picker
That is not a "file picker". There is no requirement that every DocumentsProvider on the device be serving documents that happen to be files.
I want to pick a custom extension file whose mime type is not known in android's MimeTypeMap like for example .qgs or .dcm files
There is no requirement that every DocumentsProvider on the device be using documents that have filenames for their files. The names that you see are "display names", and while for some providers they will be filenames, for other providers they can be anything that the provider wants.
is either of those options doable
No.
or are there other approaches I missed without coding my own file picker?
If you only want files, there are plenty of existing file picker implementations available in libraries. ACTION_OPEN_DOCUMENT is there for when you want to work with all possible document sources (removable storage, cloud providers, etc.), not just files.

ACTION_GET_CONTENT gives wrong path

I'm using ACTION_GET_CONTENT so that the user can select text files that the rest of my code can read and deal with.
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("text/*");
startActivityForResult(Intent.createChooser(intent, "select data"), SELECT_DATA);
Above is my code so that the user can browse which works fine.
Uri DataUri = data.getData();
File FileUri = new File(DataUri.getPath());
If I convert DataUri or FileUri to a string after using getPath or getAbsolutePath, I get a completely wrong path.
The path should be /storage/emulated/0/Documents/myFile but it gives me /document/primary:Documents/myFile. I have no idea what this "primary:Documents" thing is.
The data from the intent itself already has the wrong path, any suggestions?
I'm using ACTION_GET_CONTENT so that the user can select text files
No, you are using ACTION_GET_CONTENT so that the user can select some content that has a MIME type of text/*. Where that content comes from is largely up to the user. It may or may not be a file on the filesystem, and if it is, is may or may not be a file that you could access yourself directly (e.g., internal storage of the Dropbox app, files on removable storage on Android 4.4+ devices).
The path should be /storage/emulated/0/Documents/myFile
No, it should not.
but it gives me /document/primary:Documents/myFile
What you get will vary by whatever activity handles the ACTION_GET_CONTENT request. What that activity is will depend on what the user has installed that supports ACTION_GET_CONTENT and what the user chooses out of that list of installed stuff.
Usually, you will get a content Uri (i.e., getScheme() on Uri will return content), as the Uri will point to data being served by a ContentProvider.
The data from the intent itself already has the wrong path
It has the correct path. It is simply not a path on the filesystem.
any suggestions?
Use a ContentResolver and openInputStream() to get an InputStream for the content represented by the content Uri.

How to get absolute path from a variable type Uri

I'm having trouble getting file paths from uri returned by other activities.
I found many solutions on how to convert Uri to absolute file path like this one but its always dependent on type of content such as audio in the given link.
My app provides an activity to send files of any type.. that said I set the intent filter to
mimetype */*
but when I go in file browser and select a image file from from the camera photo folder I get path like /external/images/media/288 but If I pick a music file I get Uri like external/audio/media/1336
but on the other hand if I choose a non media file type (ex.pdf or zip) then I get the absoute path already in the Uri
So as you can see Its not known what path will Uri have it depends on what file i pick for sending with my app.
one possible solution is to make cursor queries for all types of media e.g audio video and image and if they return null then check for Uri.getPath() but I think thats a very bad practice or inefficient way !
Is there any way to get absolute Path from any Uri (given the file exists) without checking for each type of uri?
I'm having trouble getting file paths from uri returned by other activities.
There does not have to be a "file path" associated with a Uri. A Uri points to a source of data, which may or may not be a file, let alone a file which you can access directly.
You can obtain an InputStream on the data associated with a Uri via a call to openInputStream() on a ContentResolver.
Is there any way to get absolute Path from any Uri
Not in any sort of a reliable fashion. Please use the Uri in the way it was intended.

Android Camera Intent saving file twice / how to avoid

I am using the easy camera intent for taking simple pictures in Android as follows:
Uri outputFileUri = Uri.fromFile( photo );
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
camera.putExtra( MediaStore.EXTRA_OUTPUT, outputFileUri );
this.startActivityForResult(camera, CAMERA_RESULT);
The problem is: The file is saved twice
Once when the picture is actually taken (then it is saved to the gallery directory)
Once when the user "accepts" the picture (then it is saved to outputFileUri)
Although I really cant imagine this fact I already read about that the creating of those duplicate files cannot be avoided. But if not, is there a possibility to get the path of the picture in the gallery directory to delete it by my app?
Just to let you guys know, I did some research and it is actually not a good idea to solve it like this - unfortunately.
Nevertheless you can do a very easy SurfaceView-Camera-Activity which will only save the data you are telling it to.

Categories