Android 10 write file to public DCIM directory, NON DEPRECATED method - android

We have an Android app where field workers take photographs which are stored on their phone and also uploaded via a web api.
If the uploads fail they do have retry mechanisms but sometimes they need to resort to pulling the images off their phone.
In order to bring the app up to Android 10 version without deprecation I was forced to write the images to an app internal directory.
The problem is that when they upgrade their app they lose their photos from the app.
(I do also copy the images to a backup directory but this is all looking a bit klutzy)
I would like to write the images to :
/storage/emulated/0/DCIM/GoTrialImages
Instead they are going to :
/storage/emulated/0/Android/data/au.come.aceware.ktr.ktr/files/DCIM/GoTrialImages/photoIntent
(where photoIntent is the activity that this is occurring in)
Here is the code I have copied and tweaked from an online article:
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String fileName = "JPEG_" + timeStamp + ".jpg";
File mediaStorageDir = new File(getExternalFilesDir(Environment.DIRECTORY_DCIM + File.separator +"GoTrialPhotos"), TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
Log.d(TAG, "failed to create directory");
}
// Return the file target for the photo based on filename
File file = new File(mediaStorageDir.getPath() + File.separator + fileName);
Uri bmpUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
Here is my file provider entry in the manifest:
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>```
and here is #xml/provider_paths:
```<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>```
1) Is it possible to do what I am seeking to do ?
2) How do I do it without using deprecated code
Many thanks in advance
Tony
Following the suggestion to use media store I kept most of the code for creating the app internal file name
(mainly because I wanted the randomised display name):
private File createImageFileV2() throws IOException
{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
imageFileNameToUseAtWebServerEnd = strTrial + "_" + timeStamp + "_" + strUserId + ".jpg";
File[] storageDir = getExternalMediaDirs();
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir[0] /* directory */
);
return image;
}
I then passed the file object in to the following code:
public Uri testgetPhotoFileUri2(File f)
{
Uri uri = null;
String strDisplayName = f.getName();
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, strDisplayName);
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM );
final ContentResolver resolver = thisContext.getContentResolver();
try
{
final Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
uri = resolver.insert(contentUri, values);
if (uri == null)
throw new IOException("Failed to create new MediaStore record.");
return uri;
}
catch (IOException e)
{
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
}
return uri;
}
I then used the resulting uri as my camera uri:
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
However, when the OnActivityResult calls HandleBigCameraPhoto and attempts to extract the bitmap using the CameraUri:
private void handleBigCameraPhoto() {
Bitmap bitmap = null;
if (cameraUri != null)
{
if (Build.VERSION.SDK_INT >= 29) {
ImageDecoder.Source source = ImageDecoder.createSource(getApplicationContext().getContentResolver(), cameraUri);
try {
bitmap = ImageDecoder.decodeBitmap(source);
} catch (IOException e) {
e.printStackTrace();
}
It error traps to "no such file or directory"
Does this mean that I need to most of my work (image resizing, rotation, etc) using my app private file only and then as a last step insert the bitmap in to media store (and then delete the app private file so the user does not see the file name twice under gallery, recents)?

You will not make use of a deprecated function:
File file = new File(getExternalFilesDir(null)
.getParentFile()
.getParentFile()
.getParentFile()
.getParentFile(), "DCIM");
;-).

Related

How to find images on Android device using Android File Explorer?

I would like, as title suggests to find an image via Android File Explorer. I'm taking a photo and saving in on external storage but that part of storage is only available to app itself.
Basically, before I start Camera activity I have following code:
public File getPhotoFile(Contact contact, Context context) {
File externalFilesDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
if (externalFilesDir == null) {
return null;
}
File f = new File(externalFilesDir, contact.getPhotoFilename());
return f;
}
Method in Contact class getPhotoFilename():
public String getPhotoFilename() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
return "IMG_" + this.getId() + "_" + timeStamp + ".jpg";
}
And in activity:
cameraButton.setOnClickListener((View v) -> {
File f = getPhotoFile(newContact, this);
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.email.fileprovider",f);
captureImage.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(captureImage, REQUEST_TAKE_PHOTO);
});
I display full path, like this:
Log.i("PATH", filePath); //file path is member variable which just holds f.getAbsoluthPath();
What I get in output is:
/storage/emulated/0/Android/data/com.example.email/files/Pictures/IMG_0_20200428_001544.jpg
I've tried find that image in File Device Explorer, but no luck.

How to change dynamically folder name file paths Android Studio?

I need your help.
I'm starting to create an app, and I would save photos, videos, vocal memo etc. like a post/note and everything are part of this post.
These multimedia files, will be saved in a folder, which name is a timestamp. On this way, every post/note is charaterized by the timestamp. The timestamp should update every time when I want to create a new post.
I want to save my files by following this path:
Name_app
Timestamp
Pictures
Pictures1
Pictures2
...
Videos
Video1
...
Below my code.
This is the code which create my Image File (and it look like to work):
private File createImageFile () throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File root = new File(Environment.getExternalStorageDirectory().toString());
File myDir = new File(root + "/Urban_stories_sharing/" + timeStamp + "/Pictures");
if (!myDir.exists()) {
myDir.mkdirs();
}
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
myDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
This is the code which start the activity:
public void goToCamera(View v) {
getCameraPermission();
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
isStoragePermissionGranted();
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.andreacarubelli.urbanstoriessharing.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
I think that the problem is on "Uri photoURI = FileProvider.getUriForFile(this,
"com.example.andreacarubelli.urbanstoriessharing.fileprovider",
photoFile);"
This is the code of my path (and I don't know how to change it dynamically like this -> "Urban_stories_sharing/" + timeStamp + "/Pictures"):
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Urban_stories_sharing/Pictures" />
</paths>
This is my provider in my Android Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.andreacarubelli.urbanstoriessharing.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
Thank you so much.

Why aren't images taken using a camera intent saved to my phone?

Edit
My functions now look like this:
p
If uou want to save image in your external storage use Externalstoragepublicdirectory function.
This intent expects the EXTRA_OUTPUT location in Uri format, see https://developer.android.com/reference/android/provider/MediaStore.html#ACTION_IMAGE_CAPTURE. Some devices may understand an absolute file path, but this is not the documented behavior.
You can use getExternalMediaDirs() to avoid many permission restrictions.
I have made some changes to your code:
Add this to you application level in your Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Then create file_paths.xml file:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_cache" path="." />
<external-path name="my_images" path="Pictures/" />
</paths>
Then in your code:
private static final String FILE_PROVIDER_AUTHORITY = "com.example.android.fileprovider";
private String mTempPhotoPath;
public void takePhoto() {
// Create the capture image intent
Intent imageCapture= new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (imageCapture.resolveActivity(getPackageManager()) != null) {
// Create the temporary File where the photo should go
File photoFile = null;
try {
photoFile = createTempImageFile(this);
} catch (IOException ex) {
// Error occurred while creating the File
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
// Get the path of the temporary file
mTempPhotoPath = photoFile.getAbsolutePath();
// Get the content URI for the image file
Uri imageUri = FileProvider.getUriForFile(this,
FILE_PROVIDER_AUTHORITY,
photoFile);
// Add the URI so the camera can store the image
imageCapture.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
}
static File createTempImageFile(Context context) throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
Locale.getDefault()).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getExternalCacheDir();
return File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
}
//To save image
String mCurrentPhotoPath = null;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "IMG_" + timeStamp + "_";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/DCIM/Camera");
Log.d(TAG, "createImageFile: Saving image to:" + storageDir);
File image = new File(storageDir, imageFileName);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
//Return the saved image
return mCurrentPhotoPath ;
}

Android Studio App Edited images not showing up in gallery

I created a simple android app for adding frames and cropping images, now after the editing is done the pictures are saved in android root>app folder but non of these pictures show up in the gallery, here is My code
/**
* Create a File for saving an image or video
*/
private File getOutputMediaFile() {
// To be safe, you should check that the SDCard is mounted
// File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
// + "/Android/data"
// + getApplicationContext().getPackageName()
// + "/Files" + Global.AppFolder);
File root = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "/" + Global.AppFolder);
// Create the storage directory if it does not exist
if (!root.exists()) {
if (!root.mkdirs()) {
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
File mediaFile;
String mImageName = "MI_" + timeStamp + ".jpg";
mediaFile = new File(root.getAbsolutePath(), mImageName);
return mediaFile;
}
private String getRealPathFromURI(Uri uri) {
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor == null) {
return uri.getPath();
} else {
cursor.moveToFirst();
String s = cursor.getString(cursor.getColumnIndex("_data"));
cursor.close();
return s;
}
}
By the way I am a newbie and don't have a lot of experience with android coding so detailed answers and references(for further learning) shall be highly appreciated.
try this add below code after saving image
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, "title");
values.put(Images.Media.DESCRIPTION, "description");
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(Images.ImageColumns.BUCKET_ID, root.toString().toLowerCase(Locale.US).hashCode());
values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, root.getName().toLowerCase(Locale.US));
values.put("_data", root.getAbsolutePath());
ContentResolver cr = getContentResolver();
cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Add these code after you save the image
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
The Media Scanning is only done when the system Bootup. So you need to tell the media scanner to re-scan the files.

Error: open failed: ENOENT (No such file or directory)

I was trying to create a file to save pictures from the camera, it turns out that I can't create the file.
But I really can't find the mistake. Can you have a look at it and give me some advice?
private File createImageFile(){
File imageFile=null;
String stamp=new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File dir= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String imageFileName="JPEG_"+stamp+"_";
try {
imageFile=File.createTempFile(imageFileName,".jpg",dir);
} catch (IOException e) {
Log.d("YJW",e.getMessage());
}
return imageFile;
}
And I have added the permission.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
The method always gives such mistakes:
open failed: ENOENT (No such file or directory)
The Pictures directory might not exist yet. It's not guaranteed to be there.
In the API documentation for getExternalStoragePublicDirectory(), the code ensures the directory exists using mkdirs:
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File file = new File(path, "DemoPicture.jpg");
try {
// Make sure the Pictures directory exists.
path.mkdirs();
...so it may be as simple as adding that path.mkdirs() to your existing code before you createTempFile.
when a user picks a file from the gallery, there is no guarantee that the file that was picked was added or edited by some other app. So, if the user picks on a file that let’s say belongs to another app we would run into the permission issues. A quick fix for that is to add this code in the AndroidManifest.xml file:
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
Note: For Android 11 refer Scope storage Enforcement Policy https://developer.android.com/about/versions/11/privacy/storage
Replace:
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
With:
private File createImageFile() throws IOException {
// Create an image file name
make sure you call:
mkdirs() // and not mkdir()
Here's the code that should work for you:
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = new File(Environment.getExternalStorageDirectory().toString(), "whatever_directory_existing_or_not/sub_dir_if_needed/");
storageDir.mkdirs(); // make sure you call mkdirs() and not mkdir()
File image = File.createTempFile(
imageFileName, // prefix
".jpg", // suffix
storageDir // directory
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
Log.e("our file", image.toString());
return image;
}
I had a bad experience following the example given in Android Studio Documentation and I found out that there are many others experiencing the same about this particular topic here in stackoverflow, that is because even if we set
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
the problem persists in some devices.
My experience was that the example worked when I tried it in debug mode, after that 3 more tests it so happened that my SD suddenly was corrupted, but I don't think it has to do with their example (funny). I bought a new SD card and tried it again, only to realize that still both release and debug mode did the same error log: directory does not exist ENOENT. Finally, I had to create the directories myself whick will contain the captured pictures from my phone's camera. And I was right, it works just perfect.
I hope this helps you and others out there searching for answers.
A quick fix for that is to add this code in the AndroidManifest.xml file:
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
Note: Applicable for API level 29 or Higher
I used the contentResolver with the URI and it worked for me. Saw it in another SO post which i can't find.
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) {
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
hope it helps....
I have solved like this:
public Intent getImageCaptureIntent(File mFile) {
Intent mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", mFile);
mIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
// The tip is this code below
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(mIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return mIntent;
}
If you are using kotlin then use below function.
you have to provide a path for storing image, a Bitmap (in this case a image) and if you want to decrease the quality of the image then provide %age i.e 50%.
fun cacheLocally(localPath: String, bitmap: Bitmap, quality: Int = 100) {
val file = File(localPath)
file.createNewFile()
val ostream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, ostream)
ostream.flush()
ostream.close()
}
hope it will work.
Try this:
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName="JPEG_"+stamp+".jpg";
File photo = new File(Environment.getExternalStorageDirectory(), imageFileName);
return photo;
}
File dirPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File imageFile = new File(dirPath, "YourPicture.jpg");
try {
if(!dirPath.isDirectory()) {
dirPath.mkdirs();
}
imageFile.createNewFile();
} catch(Exception e) {
e.printStackTrace();
}
I got same error while saving Bitmap to External Directory and found a helpful trick
private void save(Bitmap bitmap) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = timeStamp + ".png";
String path = MediaStore.Images.Media.insertImage(activity.getContentResolver(), bitmap, imageFileName, null);
Uri uriimage = Uri.parse(path);
// you made it, make fun
}
But this have a drawback i.e. you cant change the Directory it always save images to Pictures directory but if you got it fixed fill free to edit my code:
Haa-ha-ha {I can't use emojis with my keyboard}, Good Day
Following are fixes i found
first add these two lines in your AndroidManifest file
Than add the below line just after setContentView method
ActivityCompat.requestPermissions(FullImageActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE);
and for saving the images in gallery use the below code
private void SaveImageToGallery() {
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
FileOutputStream outputStream = null;
File file = Environment.getExternalStorageDirectory();
File dir = new File(file.getAbsolutePath()+"/folderName");
dir.mkdirs();
String filename = String.format("%d.jpg",System.currentTimeMillis());
File outfile = new File(dir,filename);
try{
outputStream = new FileOutputStream(outfile);
bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
outputStream.flush();
outputStream.close();
}catch(Exception e){
Log.d("SavingError", "SaveImageToGallery: "+e.getMessage());
}
Toast.makeText(this, "Image saved in folderName folder", Toast.LENGTH_SHORT).show();
}

Categories

Resources