I'm trying to take a photo with standard camera app and save it to a storage visible only to my app.
public void startCamera(View view)
{
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null)
{
File imageFile = null;
try
{
imageFile = createImageFile();
}
catch (IOException ex)
{
ex.printStackTrace();
}
if(imageFile != null)
{
Uri imageUri = FileProvider.getUriForFile(this, "vasiljevic.filip.secretnotes", imageFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(takePictureIntent, 1);
}
}
}
createImageFile method:
private File createImageFile() throws IOException
{
String timeStamp = new SimpleDateFormat("yyyyddMM_HHmmss").format(new Date());
EditText editText = (EditText) findViewById(R.id.txtNoteName);
String noteName = editText.getText().toString();
String imageFileName = noteName + "_" + timeStamp;
File storageDir = new File(getFilesDir(), "images");
if(!storageDir.exists())
{
storageDir.mkdir();
}
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
return image;
}
This is part of the manifest containing FileProvider:
<provider
android:authorities="vasiljevic.filip.secretnotes"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
And file_paths.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
</paths>
After I take a photo, it displays the message "camera has stopped" if I run it on the emulator or "gallery has stopped" if I run it on real device. Later if I try to access it with ACTION_VIEW intent it says "can't open image" if I try it on a real device or if I try it on emulator it just behaves as if it had opened an image but the screen is all black and no real image is displayed.
Am I doing it right? is this code supposed to save the image properly? It's mostly based on Taking photos simply official android tutorial: Taking photos simply
Add FLAG_GRANT_WRITE_URI_PERMISSION to the Intent. Right now, your third-party app has no rights to save the photo to your Uri.
If you are supporting older than Android 5.0, you might also need to use a ClipData workaround, as Intent flags do not affect EXTRA_OUTPUT on older Android versions.
Related
I'm trying to capture a new image using the camera and after that store it in the external storage.
I followed this tutorial Save the full-size photo and this Add the photo to a gallery but after running the app, The camera starts and captured the image successfully, But when I go to the gallery, Can't find the image who captured via camera!
//I changed this from com.example.android.fileprovider to com.test.app.fileprovider
android:authorities="com.test.app.fileprovider"
//I changed this line Uri photoURI = FileProvider.getUriForFile(this, "com.example.android.fileprovider", photoFile); to below code
Uri photoURI = FileProvider.getUriForFile(this, "com.test.app.fileprovider", photoFile);
//I added this permission
<uses-permission android:name="android.permission.CAMERA" />
//I enabled storage permission read and write
//I tested the codes on API 24
//minSdkVersion 21
Manifests
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.app">
<uses-feature android:name="android.hardware.camera"
android:required="true" />
<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"
android:maxSdkVersion="18" />
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.test.app.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
</manifest>
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="Pictures" />
</paths>
MainActivity
public class MainActivity extends AppCompatActivity {
String currentPhotoPath;
int REQUEST_IMAGE_CAPTURE = 100;
#Override
protected void onCreate(Bundle savedInstanceState) {
SplashScreen.installSplashScreen(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dispatchTakePictureIntent();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable #org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(currentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
}
private void dispatchTakePictureIntent() {
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
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.test.app.fileprovider",
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
currentPhotoPath = image.getAbsolutePath();
return image;
}
}
Additional Question: What is the difference between this tutorial Save the full-size photo and this tutorial Add the photo to a gallery?
The best answer will take +50 bounty from this account if I reached 50 points, Otherwise, I'll give him the bounty from my other account after 2 days.
Your file will not be scanned by the MediaStore and hence will not be visible in Gallery apps as Gallery apps mostly get their info from MediaStore.
getExternalFilesDir() is a location private for your app and the MediaStore respects that.
How can I capture a new image using the camera and after that store it in the external storage?
Wrong problem description.
Once the camera app took the picture the camera app will save the picture to the file indicated by the file provider. There is nothing to do more then.
So before starting the camera and even before using FileProvider you should have determined a suitable location for your file and builded a nice uri for that file.
You have at least two options.
Use MediaStore to get an uri for a file in public DCIM or Pictures directory.
Use getExternalStoragePublicDirectory() with FileProvider to get an uri for a file in the same public directories.
I'm unable to store captured image in (getExternalFilesDir(Environment.DIRECTORY_PICTURES)) Android 11 device.
I have added
<uses-permissionandroid:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> in manifest and all file access also. But it's not working.
if (Build.VERSION.SDK_INT >= 30) {
if (!Environment.isExternalStorageManager()) {
try {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.addCategory("android.intent.category.DEFAULT")
intent.data = Uri.parse(String.format("package:%s", applicationContext.packageName))
startActivityForResult(intent, 2296)
} catch (e: Exception) {
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION
startActivityForResult(intent, 2296)
}
}
}
This code is working below Android 11 device. But on Android 11 file is not creating File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) .toString() + "/" + FolderName )
Your phone's camera doesnot have permission to write in the specified location. So to fix this, you need to use file provider and give it appropriate permissions so that the camera can write the image to your file.
To do that,
create a FileProvider. In your manifest file, add:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" /> // <-------- see this
</provider>
Now create a files.xml file in your res/xml folder. In it, write some code:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path
name="camera"
path="Camera/" />
<cache-path
name="cache"
path="/" />
<files-path
name="files"
path="." />
<external-path
name="external"
path="." />
<external-files-path
name="my_images"
path="/"/>
// todo: add necessary folders according to your requirements...
// also, this is an old example. Consider googling for the latest style. I'm just copying from an old project I have, and it kinda works...
</paths>
So here we are giving the FileProvider the folders that can be shared with external apps.
2. Now create a uri where you want to store the photo. in your activity:
Context applicationContext = getApplicationContext();
File root = getCachedDir(); // consider using getExternalFilesDir(Environment.DIRECTORY_PICTURES); you need to check the file_paths.xml
File capturedPhoto = new File(root, "some_photo.jpeg");
if(!photoFile.exists()) {
photoFile.mkdirs();
}
Uri photoURI = FileProvider.getUriForFile(applicationContext, applicationContext.getPackageName() + ".fileprovider", capturedPhoto);
Please note that my project needed to save picture temporarily, so I had used cachedDir. If you save photo permanently, use getExternalFilesDir(Environment.DIRECTORY_PICTURES); and modify file_paths.xml properly.
Now that we have the correct uri, we can call the camera intent:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,photoURI);
startActivityForResult(takePictureIntent, REQUEST_CODE);
Finally, in activty result, do something:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
// todo: maybe show photo in an imageView
}
}
I hope this works.
Edit
If you are using this app in production, relying on android's default camera app is a bad idea. Our app previously used this way, and it works with, say, samsung's defaul camera. But a lot of our users used third party apps, such as PixArt, which doesnot save photo to our given location. So we had to implement a builtin camera using CameraX. So consider using CameraX or some other camera library.
First Thing is "android.permission.MANAGE_EXTERNAL_STORAGE" Permission has no relation with saving image.
after Android 11 google say you should do your business in your space.
that mean you cant get or save image or any file as you did before Android 11.
you can only save in Shared folder or in your application storage data/App packagename/.....
if you want to access other app files then you need "android.permission.MANAGE_EXTERNAL_STORAGE" but google say this must be app prior functionality like filemanager or virus scanner like app.
As far as your App concern you havent provide save code.
in Android 11 i am suggestion using Media Api
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, s);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + getResources().getString(R.string.app_name) + File.separator + "imgfolder");
contentValues.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
contentValues.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
try {
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
contentValues.clear();
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
resolver.update(imageUri, contentValues, null, null);
}
this is for image file
use this code for save captured image
String mPath = Environment.getExternalStorageDirectory() + "/Print";
Bitmap tmp = BitmapFactory.decodeFile(mPath);
File imageFile = new File(mPath);
FileOutputStream outStream;
try
{
outStream = new FileOutputStream(imageFile);
try
{
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outStream);
outStream.flush();
outStream.close();
} catch (IOException e)
{
e.printStackTrace();
}
} catch (Exception e)
{
e.printStackTrace();
}
Im using this method to save a image to my files folder in the app internal storage.
public static String saveToInternalStorage(Context ctx,Bitmap bitmapImage){
Date date = new Date();
File mypath = new File(ctx.getFilesDir(), "ATM_"+date.getTime()+".jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
bitmapImage.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return mypath.getAbsolutePath();
}
In another option of the app i want to open the image in the android gallery and i try to do so with this code
Intent intent = new Intent();
// set flag to give temporary permission to external app to use your FileProvider
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// generate URI, I defined authority as the application ID in the Manifest, the last param is file I want to open
File file = new File(path);
if(file.exists() && file.isFile()) {
Uri uri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID, file);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(intent, 1);
}
I also have this declared on my manifest
<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/file_provider_paths"/>
</provider>
And my file_provider_paths looks like this
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="files" path="/"/>
What happens is that when i press the button to open the image on the gallery , i see the application picker for that file type , and when i choose gallery it opens and closes right after.
Question 1: Why is this happening?
Question 2: How to i open the gallery to show all of the images on the "files" folder from internal storage?
I am trying to save an image in a folder named "appFolder" using android camera.My target sdk is 25.My device is running on android nougat. However,when i click the image using "dispatchTakePictureIntent()". The image doesn't get saved in appFolder.It gets saved in DCIM/camera folder. Why is this happening and how to save this in my custom folder?
private void dispatchTakePictureIntent() {
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
Log.i("imageCaptutreError", ex.getMessage());
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.abc.def",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
private File createImageFile() throws IOException {
File folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "appFolder");
if (!folder.exists()) {
folder.mkdir();
}
File tempFile = new File(folder, "temp_image.png");
/*new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "appFolder" + File.separator + "temp_image.png");*/
mCurrentPhotoPath = tempFile.getAbsolutePath();
return tempFile;
}
Provider in Mainifest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.abc.def"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"></meta-data>
</provider>
#xml/file_paths
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="appFolder/" />
</paths>
Partly, it is because you did not call addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) on the Intent. As it stands, the other app does not have write access to the location identified by your Uri.
However, do bear in mind that third-party camera apps have bugs. Ideally, they honor EXTRA_OUTPUT. Some, however, will not:
...because they ignore EXTRA_OUTPUT in general, or
...because they do not know how to deal with the content scheme on the Uri in EXTRA_OUTPUT (even Google's own camera app had this problem until mid-2016)
FWIW, this sample app shows using ACTION_IMAGE_CAPTURE together with FileProvider.
Since I had bugs on different devices I decided to changed the whole open camera and save picture code. I have used the exact same code in the Android tutorial.
My code:
private static File createImageFile(Activity activity) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
photoPath = image.getAbsolutePath();
return image;
}
private static void dispatchTakePictureIntent(Activity activity) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = MainActivity.createImageFile(activity);
} catch (IOException ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
imageUri = FileProvider.getUriForFile(activity,
"com.APPPACKAGE.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
activity.startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
On the manifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.APPPACKAGE.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
Using this works great on a Android 6 Marshmallow, on Android 4.4 Kitkat it doesn't. On Kitkat my onActivityResult i recieve from the camera result = 0 which is RESULT_CANCELLED.
I've checked if the camera was able to save the photo on the location specified in the file_paths.xml and it didn't. this folder is filled with 0 bytes files.
What can i do to fix it?
Not all camera apps will support content as a scheme for the EXTRA_OUTPUT Uri. Google's own camera app did not support it until the summer of 2016, for example. And, since we are passing the Uri via an extra, we have no means of limiting ourselves to camera apps that support content.
Your main options are:
Reduce your targetSdkVersion below 24 and stick with Uri.fromFile(), rather than using FileProvider
Use StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build()); to disable the FileUriExposedException, then stick with Uri.fromFile(), rather than using FileProvider
Scrap your use of ACTION_IMAGE_CAPTURE entirely, switching to the camera APIs directly or via some helper library (e.g., mine)
Tactically, you might get better results if you use setClipData() to force granting of permissions on your Uri.
Thanks to #CommonsWare i added this code and it works:
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) {
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
else if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN) {
ClipData clip=
ClipData.newUri(activity.getContentResolver(), "A photo", imageUri);
takePictureIntent.setClipData(clip);
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
else {
List<ResolveInfo> resInfoList=
activity.getPackageManager()
.queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
activity.grantUriPermission(packageName, imageUri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}