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 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.
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.
I am developing a simple gallery app in which I search for folders which contains images and then show images of that particular folder. First I am using Environment.getExternalStorageDirectory() and then recursively search for folders having images. It is working fine simulator and devices. When my client installed app on Nexus 4, nothing is being loaded. I have seen some posts which says that Nexus series don't have any external SD slot. I don't have Nexus 4 for debugging and client is also non-technical. He can troubleshoot on his own to find the cause of problem. Can anybody help in this regard? I think the problem is in Environment.getExternalStorageDirectory() call which is not applicable in Nexus. Any idea how I can tackle this issue?
Here is the code snipped I am using:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File cacheDir = null;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
cacheDir=new File(android.os.Environment.getExternalStorageDirectory().getAbsolutePath(), "MyImages");
}
else {
cacheDir = getCacheDir();
}
if(!cacheDir.exists()) {
cacheDir.mkdir();
}
// File storageDir = Environment.getExternalStoragePublicDirectory(
// Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
cacheDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = "" + image.getAbsolutePath();
return image;
}
public 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
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
And here the permissions I have added manifest:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
Thanks!
I think you should give a try to the MediaStore.Images component. Load the images using a cursor. See documentation: http://developer.android.com/reference/android/provider/MediaStore.html
I try to export a bitmap from my app using share intent without saving a file for a temporal location. All the examples I found are two-step
1) save to SD Card and create Uri for that file
2) start the intent with this Uri
Is it possible to make it without requiring WRITE_EXTERNAL_STORAGE permission, saving the file [and removing it afterwards]? How to address devices without ExternalStorage?
I had this same problem. I didn't want to have to ask for the read and write external storage permissions. Also, sometimes there are problems when phones don't have SD cards or the cards get unmounted.
The following method uses a ContentProvider called FileProvider. Technically, you are still saving the bitmap (in internal storage) prior to sharing, but you don't need to request any permissions. Also, every time you share the bitmap the image file gets overwritten. And since it is in the internal cache, it will be deleted when the user uninstalls the app. So in my opinion, it is just as good as not saving the image. This method is also more secure than saving it to external storage.
The documentation is pretty good (see the Further Reading below), but some parts are a little tricky. Here is a summary that worked for me.
Set up the FileProvider in the Manifest
<manifest>
...
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
...
</application>
</manifest>
Replace com.example.myapp with your app package name.
Create res/xml/filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="images/"/>
</paths>
This tells the FileProvider where to get the files to share (using the cache directory in this case).
Save the image to internal storage
// save bitmap to cache directory
try {
File cachePath = new File(context.getCacheDir(), "images");
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
Share the image
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.myapp.fileprovider", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
Further reading
FileProvider
Storage Options - Internal Storage
Sharing Files
Saving Files
I try to export a bitmap from my app using share intent without saving a file for a temporal location.
In theory, this is possible. In practice, it is probably not possible.
In theory, all you need to share is a Uri that will resolve to the bitmap. The simplest approach is if that is a file that is directly accessible by the other application, such as on external storage.
To not write it to flash at all, you would need to implement your own ContentProvider, figure out how to implement openFile() to return your in-memory bitmap, and then pass a Uri representing that bitmap in the ACTION_SEND Intent. Since openFile() needs to return a ParcelFileDescriptor, I don't know how you would do that without an on-disk representation, but I have not spent much time searching.
Is it possible to make it without requiring WRITE_EXTERNAL_STORAGE permission, saving the file [and removing it afterwards]?
If you simply do not want it on external storage, you can go the ContentProvider route, using a file on internal storage. This sample project demonstrates a ContentProvider that serves up a PDF file via ACTION_VIEW to a PDF viewer on a device; the same approach could be used for ACTION_SEND.
If anyone still looking for easy and short solution without any storage permission (Supports nougat 7.0 as well). Here it is.
Add this in Manifest
<provider
android:name="android.support.v4.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>
Now create provider_paths.xml
<paths>
<external-path name="external_files" path="."/>
</paths>
Finally Add this method to your activity/fragment (rootView is the view you want share)
private void ShareIt(View rootView){
if (rootView != null && context != null && !context.isFinishing()) {
rootView.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(rootView.getDrawingCache());
if (bitmap != null ) {
//Save the image inside the APPLICTION folder
File mediaStorageDir = new File(AppContext.getInstance().getExternalCacheDir() + "Image.png");
try {
FileOutputStream outputStream = new FileOutputStream(String.valueOf(mediaStorageDir));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (ObjectUtils.isNotNull(mediaStorageDir)) {
Uri imageUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", mediaStorageDir);
if (ObjectUtils.isNotNull(imageUri)) {
Intent waIntent = new Intent(Intent.ACTION_SEND);
waIntent.setType("image/*");
waIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
startActivity(Intent.createChooser(waIntent, "Share with"));
}
}
}
}
}
Update:
As #Kathir mentioned in comments,
DrawingCache is deprecated from API 28+. Use below code to use Canvas instead.
Bitmap bitmap = Bitmap.createBitmap(rootView.getWidth(), rootView.getHeight(), quality);
Canvas canvas = new Canvas(bitmap);
Drawable backgroundDrawable = view.getBackground();
if (backgroundDrawable != null) {
backgroundDrawable.draw(canvas);
} else {
canvas.drawColor(Color.WHITE);
}
view.draw(canvas);
return bitmap;
This for sharing CardView as an Image then saving it in the cache subdirectory of the app's internal storage area.
hope it will be helpful.
#Override
public void onClick(View view) {
CardView.setDrawingCacheEnabled(true);
CardView.buildDrawingCache();
Bitmap bitmap = CardView.getDrawingCache();
try{
File file = new File(getContext().getCacheDir()+"/Image.png");
bitmap.compress(Bitmap.CompressFormat.PNG,100,new FileOutputStream(file));
Uri uri = FileProvider.getUriForFile(getContext(),"com.mydomain.app", file);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.setType("image/jpeg");
getContext().startActivity(Intent.createChooser(shareIntent, "Share"));
}catch (FileNotFoundException e) {e.printStackTrace();}
}
});
Here is working method to make a screenshot of own app and share it as image via any messanger or email client.
To fix the bitmap not updating problem I improved Suragch's answer, using Gurupad Mamadapur's comment and added own modifications.
Here is code in Kotlin language:
private lateinit var myRootView:View // root view of activity
#SuppressLint("SimpleDateFormat")
private fun shareScreenshot() {
// We need date and time to be added to image name to make it unique every time, otherwise bitmap will not update
val sdf = SimpleDateFormat("yyyyMMdd_HHmmss")
val currentDateandTime = sdf.format(Date())
val imageName = "/image_$currentDateandTime.jpg"
// CREATE
myRootView = window.decorView.rootView
myRootView.isDrawingCacheEnabled = true
myRootView.buildDrawingCache(true) // maybe You dont need this
val bitmap = Bitmap.createBitmap(myRootView.drawingCache)
myRootView.isDrawingCacheEnabled = false
// SAVE
try {
File(this.cacheDir, "images").deleteRecursively() // delete old images
val cachePath = File(this.cacheDir, "images")
cachePath.mkdirs() // don't forget to make the directory
val stream = FileOutputStream("$cachePath$imageName")
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream) // can be png and any quality level
stream.close()
} catch (ex: Exception) {
Toast.makeText(this, ex.javaClass.canonicalName, Toast.LENGTH_LONG).show() // You can replace this with Log.e(...)
}
// SHARE
val imagePath = File(this.cacheDir, "images")
val newFile = File(imagePath, imageName)
val contentUri = FileProvider.getUriForFile(this, "com.example.myapp.fileprovider", newFile)
if (contentUri != null) {
val shareIntent = Intent()
shareIntent.action = Intent.ACTION_SEND
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // temp permission for receiving app to read this file
shareIntent.type = "image/jpeg" // just assign type. we don't need to set data, otherwise intent will not work properly
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
startActivity(Intent.createChooser(shareIntent, "Choose app"))
}
}