I'm trying to implement a camera activity where the user takes a picture and saves it to the phone.
Error:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/no.test.group_project/files/Pictures/JPEG_20200423_114453_750680396837474735.jpg
AndroidManifest.xml
android:name="androidx.core.content.FileProvider"
android:authorities="no.test.group_project.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Android/data/no.test/group_project/files/Pictures/" />
</paths>
Java
public void openCamera(View view) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
ex.printStackTrace();
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"no.test.group_project.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, Utils.REQUEST_IMAGE_CAPTURE);
}
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
currentPhotoPath = image.getAbsolutePath();
return image;
}
I am getting above error when I try to get the Uri from
FileProvider.getUriForFile(this,
"no.test.group_project.fileprovider",
photoFile
);
Replace path="Android/data/asmund.thomas.group_project/files/Pictures/" with path=".".
Related
I'm fairly new into saving media in android, and I'm wondering why this is giving me so much trouble. I am requiring photos taking by my app to be made public, so other apps (including the gallery) can find them.
The documentation states that it if you want an image to be accessible by other apps, you should save them on in the public external storage which is provided by getExternalStoragePublicDirectory(), with the DIRECTORY_PICTURES as the argument.
When I try to do this however, the IOException is thrown in my method, and I'm struggling to find out why.
File creation method:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
Photo capture method:
public void capturePhoto (View view) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this, "com.auswide.auswidedashboard", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, IMAGE_REQUEST);
}
}
}
Permissions and Provider code in the Manifest
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.auswide.auswidedashboard"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
And finally the path required by the provider:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.auswide.auswidedashboard/files/Pictures"/>
</paths>
The exception message reads I/System.out: java.io.IOException: Permission denied', which simply confuses me more as I though I gave it the permissions in the manifest?
Any help would be greatly appreciated.
What am I doing wrong?
I have tried googlign the problem, but people mostly say it is an issue with android Q, but I don't think my problem is that.
AndroidManifest.xml:
<manifest ...>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>
Java:
void takePhoto(){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA},2);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getPackageManager()) != null)
{
File photoFile = createPhotoFile();
if(photoFile != null){
pathToFile = photoFile.getAbsolutePath();
Uri photoURI = getUriForFile(this, this.getPackageName() + ".fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(intent, REQUEST_CAMERA);
}
}
}
private File createPhotoFile() {
String name = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File storageDir = getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = null;
try{
image = File.createTempFile(name, ".jpg", storageDir);
} catch(IOException e){
Log.d("myLog", "Except: " + e.toString());
}
return image;
}
I added a destination file to save my images in app's created folder.
static final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
In cropIwa's documentation, it needs destinationUri
Here is what I have done:
cropIwaView.crop(new CropIwaSaveConfig.Builder(Uri.fromFile(imageRoot.getAbsoluteFile()))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela
cropIwaView.crop(new CropIwaSaveConfig.Builder(getUriFromFile(this, new File(R.xml.file_paths + ".png")))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela a saveCompleteListener of cropiwa.
cropIwaView.setCropSaveCompleteListener(new CropIwaView.CropSaveCompleteListener() {
#Override
public void onCroppedRegionSaved(Uri bitmapUri) {
addPicToGallery(CropProfilePicture.this, bitmapUri);
Toast.makeText(CropProfilePicture.this, "Done", Toast.LENGTH_SHORT).show();
finish();
}
});
Create temp file where the image should save
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile(context);
} catch (IOException ex) {
// Error occurred while creating the File
Timber.e("Can't create photoFile:%s",ex.getMessage());
}
private static File createImageFile(Context ctx) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(
imageFileName, /* prefix */
".png", /* suffix */
storageDir /* directory */
);
}
Generate Uri and use FileProvide when version LOLLIPOP or above
final Uri imageUri ;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
imageUri = Uri.fromFile(photoFile); // file://
else
imageUri = getUriFromFile(context,photoFile); // FileProvider
Saving image logic
cropIwaView.setCropSaveCompleteListener(bitmapUri -> {
addPicToGallery(context, bitmapUri); // sendBroadcast to gallery to scan new added images...
});
cropIwaView.crop(new CropIwaSaveConfig.Builder(imageUri)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
Use FileProvider with android version LOLLIPOP and above
private static Uri getUriFromFile(Context context, File newFile) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", newFile);
}
Under app res add file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.your_package_id/files/Pictures" />
</paths>
manifest.xml
<application
...// other attributes
>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.your_package_id.fileprovider"
android:readPermission="com.your_package_id.fileprovider.READ"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
add permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Update
Update image on gallery.
private static void addPicToGallery(Context context, Uri contentUri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}
I have seen this question asked previously, but before you mark this question as redundant let me just say that I've been trying to figure this out for three hours and I'm just confused. My goal is to take a photo, set it to an imageview, and upload it to Firebase Storage. In order to get the full size photo, I believe I have to save it to a local file, because the FileProvider takes a reference to a file and that's where the full size photo goes. Is that correct so far? I cannot for the life of me get the FileProvider to accomplish this.
After many attempts, this is what I have:
// It all starts here
public void onClick(View v) {
Log.d("DebugMySocks", "Change photo button clicked");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
photoFile = null;
try {
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(getApplicationContext(),
getPackageName(),
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp;
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mProfileImage.setImageBitmap(imageBitmap);
}
}
// in manifest file
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
// in /xml/filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
</paths>
and the error message is:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.kevin.moresocksplease/files/Pictures/JPEG_20171214_111337.jpg706457555.jpg
Replace:
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
with:
<external-files-path name="my_images" />
I build my app with FileProvider and I want to save the image after I take it. But I can't find the image in the gallery.
I found these source codes from the Android Studio tutorial. I don't know what is the problem. I tried use debugger and I think the createFile() is correct. I also have my bitmap works. It can display the image I take but I can't add the image to the gallery.
I have this in my Manifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.temp.test"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
And in the file_paths.xml I have
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path="Android/data/com.temp.test/files/Pictures" />
</paths>
This is how I write the activity
private String mCurrentPhotoPath;
private ImageView mImageView;
private ImageButton StartCameraBtn;
private File photoFile = null;
//requestCode
private static final int REQUEST_IMAGE_CAPTURE = 1;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo_note);
mImageView = (ImageView) findViewById(R.id.imageView);
StartCameraBtn = (ImageButton) findViewById(R.id.StartCamera);
StartCameraBtn.setOnClickListener(this);
}
public void onClick(View view)
{
clearAllFocus();
switch (view.getId())
{
case R.id.StartCamera:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null)
{
try
{
photoFile = createFile();
}
catch (IOException e)
{
e.printStackTrace();
}
if(photoFile != null){
Uri photoURI = FileProvider.getUriForFile(this,
"com.temp.test",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
break;
...
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
mImageView.setImageBitmap(null);
switch (requestCode)
{
case REQUEST_IMAGE_CAPTURE:
if (resultCode == RESULT_OK)
{
setPic();
galleryAddPic();
}
break;
}
}
private File createFile() throws IOException
{
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
private void galleryAddPic()
{
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
private void setPic()
{
// Get the dimensions of the View
int targetW = mImageView.getWidth();
int targetH = mImageView.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mImageView.setImageBitmap(bitmap);
}
Please help! Thank you!
I use debugger to test the galleryaddpic() and the URI has been accessed sucessfully, but I don't know why I can't add it to the gallery. I can't find the image file in the android VM directory either.
This is the debug log:
https://i.stack.imgur.com/JN9uJ.png
I'm using chickendinner as my domian, and keep is the name of my app.
Thank you!
Call this function in onActivityResult. It worked for me!
It is in fragment.In activity you can use "this" instead of "getActivity()".
private void galleryAddPic() {
File f = new File(imageFilePath); //set your picture's path
try {
MediaStore.Images.Media.insertImage(getActivity().getContentResolver(),
f.getAbsolutePath(), f.getName(), null);
getActivity().sendBroadcast(new Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(f)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Your code handles
write new photo as a jpg-file to the filesystem
add new photo to media-db via broadcst Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
create a FileProvider that allow other apps to access private //Android/data/com.temp.test/files/Pictures/... files through a content-uri content://com.temp.test/...
I assume that the media-db scanner has no read-permission to your app-s private data directory Android/data/com.temp.test/files/Pictures via a file-uri and therefore cannot add the new photo to media-db.
Whatsapp and other apps store their received/send photo in public readable internal memory (i.e. /sdcard/PICTURES/WhatsApp/ ) where the media scanner can read it via file-uri via Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.
I donot know, if the media scanner can handle content: -uris instead of file uri-s: You can try this:
private void galleryAddPic()
{
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
// assume that mCurrentPhotoPath ="Android/data/com.temp.test/files/Pictures/myTestImage.jpg"
// can be accessed from outside as "content://com.temp.test/myTestImage.jpg"
Uri contentUri = Uri.parse("content://com.temp.test/myTestImage.jpg");
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
Please let us know if this works.
If this does not work you can try to manually insert a "content://com.temp.test/..." entry into media database.
using public readable internal memory directories should work without a fileprovider