Suppose I am writing an alternative Camera application and wish to write images exactly into the same place as Camera does and name them exactly in the same name Camera does.
How would I accomplish this?
How to know the location of camera files?
How to know current naming convention?
How to gain permissions to that directory?
Any of answer would be appreciated.
Okay, suppose it is not really camera alternative. Suppose I would like to write formats other than images, like audio, video, or something else.
How would I accomplish this?
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + File.separator + "You Dir. Name";
if you append any string to end this
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
it will create dir inside the folder
How to know the location of camera files?
By default camera uses this location
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
How to know current naming convention?
IMG_yyyyMMDD_timeStamp
How to gain permissions to that directory?
Using permission manager for Camera and External Storage permissions
You cannot write exactly into the same folder as the default camera app does. But you can make use of Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
mediaStorageDir = new File(Environment.getExternalStorageDirectory(), FOLDER_NAME);
Intent takePictureFromCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureFromCameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT,
Uri.fromFile(mediaStorageDir));
startActivityForResult(takePictureFromCameraIntent, 100);
But, please note that this will create only a sub-folder in the DCIM directory and will not store exactly where the default camera does. But you can always create sub-folders with your required folder name.
Things to keep in mind for this answer:
Every phone producer creates it's own Camera App, tailored to their hardware.
With the right permissions, App's can write (almost) everywhere they want...
Now to your question(s):
First, we don't know where the photo's are stored, and what the naming convention is. Every one and their mother has a different idea about what is best. So no "Hey it's always there".
Those who seek will find: get read & write permissions for the whole device. With that search the whole device to find in what folders the images are in. Now subtract the folders that come from "social media". My guess would be that the folder with the most and or latest images is the one that you want. To be sure, you will need testers, that trust you.
All that are found were not unorganized: just find the pattern used. There may be a standard for this. The big companies will surely have one. You can ask the device what maker it has. Note, that that answer might not be correct.
Ain't that a picture, no, it's a photo: And then you get the fun part of accessing the camera. Good times. Remember: request picture => get location of picture in memory => save picture as file. Best part, there is no enforcement of all parts of this API, so different devices need different instructions...
Have fun & Good luck!
ps, the downvotes are probably for the lack of code
Try using this code you can save the image bitmap to the directory using insert query
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), bitmap,
System.currentTimeMillis() + ".jpg", "DESCRIPTION HERE");
For more details see the link
I've found this code useful for choosing the last used DCIM/Camera folder.
String getDCIMCamera() {
try {
String[] projection = new String[]{
MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATA,
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.MIME_TYPE};
Cursor cursor = getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor != null) {
cursor.moveToFirst();
do {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
if (path.contains("/DCIM/")) {
File file = new File(path);
path = file.getParent();
cursor.close();
return path;
}
} while (cursor.moveToNext());
cursor.close();
}
}
catch (Exception e) {
}
return "";
}
Related
The main idea of the question is just same as the title - what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
In case you don't get what I meant by using cursor, the example is here.
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
The two most frequent ways were these two, but it seems a bit too complicated using cursor, while you can get the same result with one simple method, .getPath(). So, I think there must be the reason I should use the cursor in some cases, but I can't get it.
Could you explain me what it would be?
what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
A Uri is not a file. There is no "real path".
If the scheme of the Uri is file, then it represents a file on the filesystem that, in theory, your app should be able to access. Use getPath() to get the filesystem path.
If the scheme is anything else, it does not necessarily represent a file on the filesystem that your app can access. For example, if the scheme is http or https, the Uri represents something that would be downloaded from a Web server.
If the scheme is content, then it is backed by a ContentProvider. Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.
If the scheme is content and you specifically obtained the Uri from the MediaStore, then perhaps your Cursor approach will give you a path. It also might give you null, and the path that you get may not be accessible to you (just because the system's MediaStore can index a file does not imply that your app has access to that same file). This is worse on Android 10, where you do not have read access to external storage by default. Hence, this technique is unreliable and should not be used.
Beyond that, though, you cannot make any assumptions about what data is used to support that content Uri. It could be:
A local file on external storage
A local file on internal storage for the other app (e.g., served by FileProvider)
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly by the ContentProvider
A stream of bytes held in a BLOB column in a database that needs to be served by the ContentProvider
A piece of content that needs to be downloaded by the other app first (e.g., Dropbox)
...and so on
So, to recap: a Uri is not a file. There is no "real path".
The main idea of the question is just same as the title - what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
In case you don't get what I meant by using cursor, the example is here.
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
The two most frequent ways were these two, but it seems a bit too complicated using cursor, while you can get the same result with one simple method, .getPath(). So, I think there must be the reason I should use the cursor in some cases, but I can't get it.
Could you explain me what it would be?
what is the difference between .getPath() vs cursor, when you get the real path of a file from uri in Android?
A Uri is not a file. There is no "real path".
If the scheme of the Uri is file, then it represents a file on the filesystem that, in theory, your app should be able to access. Use getPath() to get the filesystem path.
If the scheme is anything else, it does not necessarily represent a file on the filesystem that your app can access. For example, if the scheme is http or https, the Uri represents something that would be downloaded from a Web server.
If the scheme is content, then it is backed by a ContentProvider. Use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri.
If the scheme is content and you specifically obtained the Uri from the MediaStore, then perhaps your Cursor approach will give you a path. It also might give you null, and the path that you get may not be accessible to you (just because the system's MediaStore can index a file does not imply that your app has access to that same file). This is worse on Android 10, where you do not have read access to external storage by default. Hence, this technique is unreliable and should not be used.
Beyond that, though, you cannot make any assumptions about what data is used to support that content Uri. It could be:
A local file on external storage
A local file on internal storage for the other app (e.g., served by FileProvider)
A local file on removable storage
A local file that is encrypted and needs to be decrypted on the fly by the ContentProvider
A stream of bytes held in a BLOB column in a database that needs to be served by the ContentProvider
A piece of content that needs to be downloaded by the other app first (e.g., Dropbox)
...and so on
So, to recap: a Uri is not a file. There is no "real path".
I am using JAudioTagger library for reading and writing tags for an audio file. I am able to read the tags but unable to write them.
I am retrieving audio file path like this :
private String getSongPath(long songId) {
String path = null;
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.Media.DATA};
String selection = MediaStore.Audio.Media._ID + " == ?";
String[] selectionArgs = {String.valueOf(songId)};
Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
if (cursor != null) {
int pathCol = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
path = cursor.getString(pathCol);
cursor.close();
}
return path;
}
Then to write tags using JAudioTagger :
File songFile = new File(path); // path looks like /storage/3932-3434/Music/xyz.mp3
AudioFile audiofile = = AudioFileIO.read(songFile);
Tag tag = = audiofile.getTag();
tag.setField(FieldKey.TITLE, title);
// some more setField calls for different feilds
audiofile.commit();
The commit() method is giving following Exception :
org.jaudiotagger.audio.exceptions.CannotWriteException:
java.io.IOException: Cannot make changes to file xyz.mp3 at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:799) at
com.techapps.musicplayerplus.MainActivity$17.onClick(MainActivity.java:2125)
at
android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157)
at android.os.Handler.dispatchMessage(Handler.java:102) at
android.os.Looper.loop(Looper.java:148) at
android.app.ActivityThread.main(ActivityThread.java:5417) 06-18
10:59:48.134 8802-8802/com.techapps.musicplayerplus W/System.err:
at java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused
by: java.io.IOException: Cannot make changes to file Saibo.mp3 at
org.jaudiotagger.audio.mp3.MP3File.precheck(MP3File.java:824) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:850) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:783) at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:795)
I am running this code on Android 6 while my app is targeted at SDK 22. I have also mentioned following permission in manifest.
android.permission.WRITE_EXTERNAL_STORAGE
Still I am unable to write to SD card. Please help me. Thanks in advance.
You have to use Storage Access Framework (SAF) to access SD Card from API 19 (Kitkat) onward.
First we need to ask user to provide a URI of the folder we want to access. If we want access to entire SD card, user needs to provide URI of SD card's root folder.
For example, when user hits Edit button, we have to first show hint dialog box, asking user to select required directory in SD Card which we want to access. You can display following image in hint dialog box to ask user to select root directory of SD Card :
When user dismisses hint dialog box, you need to trigger Storage Access Framework :
private void triggerStorageAccessFramework() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}
public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
Uri treeUri = null;
// Get Uri from Storage Access Framework.
treeUri = resultData.getData();
pickedDir= DocumentFile.fromTreeUri(this, treeUri);
if (!isSDCardRootDirectoryUri(treeUri)) {
Toast.makeText(this, "Wrong directory selected. Please select SD Card root directory.", Toast.LENGTH_LONG).show();
createSDCardHintDialog().show();
return;
}
// Persist URI in shared preference so that you can use it later.
SharedPreferences sharedPreferences = getSharedPreferences(App.PREFERENCE_FILENAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(App.SDCARD_URI_KEY, treeUri.toString());
editor.apply();
// Persist access permissions, so you dont have to ask again
final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
private boolean isSDCardRootDirectoryUri(Uri treeUri) {
String uriString = treeUri.toString();
return uriString.endsWith("%3A");
}
Once you get Uri of user picked directory, you can perform write operation using SAF : (creadit : this answer )
public void writeFile(DocumentFile pickedDir) {
try {
DocumentFile file = pickedDir.createFile("image/jpeg", "try2.jpg");
OutputStream out = getContentResolver().openOutputStream(file.getUri());
try {
// write the image content
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("Something went wrong : " + e.getMessage(), e);
}
}
It could be that you pointing to non existing file.
Check your path file by using Log.
Log.d("Activity", "path = " + path);
Android-M or API 23 introduced Runtime Permissions for reducing security flaws in android device.
To update your apps using Google Play services to handle Android 6.0 permissions, it’s good practice to manage the user’s expectations in setting permissions that the runtime may require. The following link will help you avoid potential issues.
https://developer.android.com/training/permissions/requesting.html
have you declared the permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ?
I saw that you already created an issue in the JAudioTagger GitHub repository which was advisable, but never got a universally working solution. My findings so far:
The answer mentioning SAF is correct, but it won't help you as SAF will provide a DocumentFile, not a File.
You might try to modify JAudioTagger to your needs, replacing File with DocumentFile, but the latter one has not all functions you will need.
Also InputStream and OutputStream will not help you, as JAudioTagger needs File and internally heavily uses RandomAccessFile which is not available either.
Google "forgot" to provide some getRandomAccessFileFromUri() which makes things even worse (Yes, there are hacks using Java reflection to work around this limitation...).
The "/proc/self/fd" method (How to handle SAF when I can only handle File or file-path?) will also not work immediately, as JAudioTagger needs copy and renaming functions that are not applicable to this kind of files. Particularly JAudioTagger will not find a suitable file name extension like ".m4a". Of course you could try to change JAudioTagger accordingly.
You might follow the advice to make a copy of the file to your personal storage, then apply JAudioTagger to it and finally copy it back to SD card, but:
If you want to use JAudioTagger to read from SD card, this will, as announced by Google, fail with Android 10. Starting with that version, you will not even have read access to the SD card via the File interface.
Further, the File interface gives you read access to SD cards with Android 9 and below, but not to other SAF devices, like USB OTG memory or SMB shares etc.
Of course you could also copy each file in order to read its metadata, but this will be awfully slow and is not suitable if you have more than a few files.
So my current advices are:
Try the "/proc/self/fd" method and modify JAudioTagger accordingly.
If the changes are too heavy, use the fd method for reading the tags and the copy method for writing.
BTW: I am currently modifying an older version of JAudioTagger for using both File and DocumentFile transparently, but the changes are tremendous, bear a high risk, need some help classes, and the work is unfinished, yet.
BTSW: The DocumentFile functions are painfully slow, compared to the File functions.
I have an application which take picture and save it in folder in sd card, after I upload it to server, my requirement is my pic taken from the application should not get displayed in device gallery as well as file manager (except some HD app) but after hiding the folder / images it must shown in my application.
I have used some solutions like adding "." before any folder name, but using this will disappear my pic from my application too so it is useless,
I also used .nomedia file concept but it is not working properly, images show in file manager.
Any help will be appreciated...
Indeed a .nomedia file in a folder will not show pictures from that folder in Gallery app. But you can never prevent a file explorer to display them. add "." before folder name it will dispear pic my application too so it useleass. That is big non sense as then you are doing something wrong in your apps code. But even then all filemanagers would display it (as all file managers will display hidden files wenn told).
The best to do is to put the files in a folder in internal memory. Then a filemanager can not see them. (At least when the phone is not rooted).
thanking you in advanced to use your time.... I hate such words.
#greenapps for add just "." in folder name it totaly hide the image from everywhere (from gallery default app as well my app to ).
but Thanks for spend your thought i found my solution for hide the image from gallery app only
here i just call this function immediate after save a pic in my app folder,
this function delete the latest pic from the default DCIM folder but yes you have to validate that if the picture is taken or not cause ;-)
if you find anything wrong kindly notify me!
public void deleteImageFromGallery(String captureimageid) {
Cursor c = null;
Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=?",
new String[]{captureimageid});
String[] projection = {MediaStore.Images.ImageColumns.SIZE, MediaStore.Images.ImageColumns.DISPLAY_NAME,
MediaStore.Images.ImageColumns.DATA, BaseColumns._ID,};
AppLog.i("InfoLog", "on activityresult Uri u " + u.toString());
try {
if (u != null) {
c = managedQuery(u, projection, null, null, null);
}
if ((c != null) && (c.moveToLast())) {
ContentResolver cr = getContentResolver();
int i = cr.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, BaseColumns._ID + "=" + c.getString(3),
null);
AppLog.i("TAG", "Number of column deleted : " + i);
}
} finally {
if (c != null) {
c.close();
}
}
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"
+ Environment.getExternalStorageDirectory())));
}
I open a picture in album and get the Uri. Then I convert the Uri to a file path. In the log it shows as something like mnt/storage/emulated/0/xxx.jpg. I covert Uri to file path as the way like:
Cursor cursor = GlobalObjectManager.getInstance().getContext().getContentResolver()
.query(filePathUri, null, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
fileName = cursor.getString(column_index);
The problem is that when I open the file with function it catches a FileNotFoundException.
String path = "mnt/storage/emulated/0/xxx.jpg";
FileInputStream in = new FileInputStream(path);
the code works well on other devices with Android 2.3-4.1.
So far as I know is that my Nexus 4 runs Android 4.2 and mnt/storage/emulated/0/ works for multi-user.
In my app I must use FileInputStream() function to read byte data of the beginning of the file.
Could anyone tell me how to fix the bug? Thanks!
ok i fix it. I made a big mistake! I add mnt/ in front of storage/ needlessly, and it takes the bug.
I believe you are seeing /storage/emulated/0. We're seeing this problem too, it seems to be related to the new /storage handling for multiple SD cards, I think it was introduced in Android 4.1 but maybe later. If you look, you'll see that /storage/emulated/0 does not exist on the filesystem, not even as a symlink. Who knows what the system is using that path or what tricks are going on there.
The workaround is to do:
fileName = new File(cursor.getString(column_index)).getCanonicalPath();
I think you should not have the "mnt" ,this is to say,you can code as this:
String path = "mnt/storage/emulated/0/xxx.jpg";
FileInputStream in = new FileInputStream(path);