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.
Related
private void share(Uri uri){
Intent waIntent = new Intent(Intent.ACTION_SEND);
waIntent.setType("audio/*");
waIntent.putExtra("jid", get_DATA.getSelectedPhonewdcountrycode() + "#s.whatsapp.net"); //phone number without "+" prefix
waIntent.setPackage("com.whatsapp");
waIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(waIntent, "Share with"));
}
I have tried many solution but every solutio takes me to whatsapp contact list to chose a contact, i want the file to be send directly to users inbox
BTW it does take me to the targeted contact after a few second delay but it toast a message File is not supported
There is no foreseeable way to upload it directly from a file to a user (unlike on laptops), unless you extend the file path to reach a particular contact e.g
com.whatsapp.jeremy
This however might still not help as WhatsApp chats are end to end encrypted, not direct, so you might have to go through some of the database files like db.crypt to find the right path to each individual.
Try seeing how these pan out for you
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.
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.
I have just written a function in an android app that deletes a file using the standard 'File' class in Java. i.e:
String fileName= "/mnt/Gallery/Img001.jpg";
File file = new File(fileName);
file.delete();
While the above procedure is simple enough, I have been wondering if there is any advantage to doing the same via a 'ContentResolver'. Any advice would be appreciated.
------------------------------------------ EDIT ----------------------------------------
Here's an example of deleting a file via the Content Resolver. This example assumes the file being deleted is an image and that its 'id' is known.
long mediaId = 155; // NOTE: You would normally obtain this from the content provider!
Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri itemUri = ContentUris.withAppendedId(contentUri, mediaId);
int rows = getContentResolver().delete(itemUri, null, null);
String path = itemUri.getEncodedPath();
if(rows == 0)
{
Log.e("Example Code:","Could not delete "+path+" :(");
}
else
{
Log.d("Example Code:","Deleted "+path+ " ^_^");
}
Android's content provider framework has certain added advantages when compared to directly manipulating data.
You can think on the lines of 'Where does the file reside and who may be deleting it'.
Scenario 1
File resides on SD card (a path accessible by your app) and you app is deleting it.
Solution : Since the path is accessible to you, the java approach will work with a file Uri like:
file://mnt/sdcard/downloads/image.jpeg
Scenario 2
File resides in another app (say dropbox) and your app needs to delete the file.
Solution : This means that the file actually resides in the private storage of another app. A file: Uri will the above approach will give you access denied. So, your app will need to fetch a content Uri from the app containing the file and call into its content provider to delete.
fileUri = Uri.parse ("content : // " + packageContainingTheFile " + fileId); // replace this with Uri obtained from the app.
getContext().getContentResolver().delete (fileUri, null, null);
Scenario 3
File resides in your app's package directory i.e, under data/data/com.yourpackage/yourfolder/yourfile.xxx and your app is the only one deleting it.
Solution : Here, either of the above approaches will work since you have the access to delete the file.
Uri will look like:
file://data/data/yourpackage/folder/file.ext
The prime advantage of using content provider here is that you automatically gain the observer model. Content provider callbacks are a well defined entry point from where data is modified. Hence, its a desired place to notify others of changes using:
getContext().getContentResolver().notify(uri, null)
Assume you have views that show a listing of such file items. As soon as the delete is done, your can be notified.
Scenario 4
File resides in your app's package directory i.e, under data/data/com.yourpackage/yourfolder/yourfile.xxx and you want to expose the delete functionality to other apps.
Solution : This is similar to Scenario 1, just the other way round. Other apps cannot delete the file in your private storage with a Uri like
file://data/data/yourpackage/folder/file.ext // works just for your app
They will need to call in your content provider to do this with a Uri like.
content://providerAuthority/delete/id
which your content provider will need to map to file.ext absolute path.
Summary
To conclude, the use of content provider is necessary is some scenarios while optional in others. It largely depends on your app requirements. If you have views, CursorLoaders in place and want to be informed about updates or wish to expose deletion of your app data to other apps, content provider is the cleanest approach.
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.