FileProvider path creation and BitmapFactory.decodefile problems in Android - android

I am trying to use BitmapFactory.decodefile() in order to create a scaled down
version of a camera photo and set it to an imageview in my framelayout.
Am following the following instructions from Android Developers:
https://developer.android.com/training/camera/photobasics.html
In these instructions, files are created and stored inside a fileprovider which holds some meta-data file formatted in xml. Somehow, BitmapFactory.decodefile() can't seem to access this file which stores a picture whose content uri resides inside the fileprovider.
The fileprovider is created inside the androidmanifest file as follows:
<provider
android:authorities="mypackagename.fileprovider"
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" >
</meta-data>
</provider>
the file_paths xml file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/mypackagename/files/Pictures/" />
</paths>
The file name for where the picture will reside is generated via this 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 = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName,".jpg",storageDir);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
Log.d("absolute",""+image.getAbsolutePath());
return image;
}
The code starts an intent in order to take a picture with startactivityforresult() like this:
Intent i = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (i.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 (this,"mypackagename.fileprovider",photoFile);
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(i,TAKE_PICTURE);
}
}
Now, onActivityForResult() method starts, but if I set my if statement like this
if(requestCode == TAKE_PICTURE && resultCode == RESULT_OK)
it doesn't work. I don't know why.
From reading the android reference documents on fileprovider class, I see that
I must open the photoUri which is passed as an extra in my intent.
According to the docs, I must open It with ContentResolver.openFileDescriptor which will return a ParcelFileDescriptor. Somehow, this is where the picture the camera just took resides. Somehow I need to access the file name from this ParcelFileDescriptor object and pass it to BitmapFactory.decodefile in order to scale down the picture-bitmap and set it on my imageview. I don't know how to go about this
When trying to scale the picture-bitmap I have the following code that returns -1, meaning that "according to the android reference docs for BitmapFactory class" "there was a problem decoding the file". I don't know why there would be a problem. Here's the code that returns -1:
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
Both photoW and photoH return -1. Remember that the variable mCurrentPhotoPath was initialized inside the method CreateImageFile() , all the way at the top of this question.
I've also tried,
BitmapFactory.decodeFile(photoFile.getAbsolutePath(), bmOptions);
But still the same result, actually mCurrentPhotoPath and photoFile.getAbsolutePath() are equal strings.
I think that somehow the fileprovider with its meta-data xml file are somehow hiding the file path from BitmapFactory.decodefile().
The picture is taken when I test the app and is also stored inside my phone picture gallery.
Please provide any advice or suggestions since I need to proceed with the tess-two library and perform OCR with the pictures from the camera.
Thanks for your suggestions

I have faced exactly the same problem and this is how I dealed with it: first of all set i.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); as suggested above, before launching the camera. Then, DO NOT use the BitmapFactory.decodeFile() method as it only works with the file:/// type of Uris retrieved using Uri.fromFile(), but not with the content:// type of Uris retrieved using FileProvider.getUriForFile() which is the required way for APIs >= 24.
Instead, use a ContentResolver to open an InputStream and decode the image as follows:
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
ContentResolver cr = getActivity().getContentResolver();
InputStream input = null;
InputStream input1 = null;
try {
input = cr.openInputStream(photoUri);
BitmapFactory.decodeStream(input, null, bmOptions);
if (input != null) {
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
try {
input1 = cr.openInputStream(photoUri);
Bitmap takenImage = BitmapFactory.decodeStream(input1);
if (input1 != null) {
input1.close();
}
} catch (Exception e) {
e.printStackTrace();
}
Note, that in order to obtain the Bitmap, you have to open a 2nd input stream, since the first one cannot be reused.

I ended here, because I was looking for a way to get Bitmap from an Uri of an image in internal storage, written with FileProvider.
(Followed this answer to write image to internal storage: https://stackoverflow.com/a/30172792/529663)
I was able to get back a readable Bitmap image using GrAndroidDeveloper's answer. Here's how:
ContentResolver cr = getApplicationContext().getContentResolver();
InputStream is = cr.openInputStream(uri);
Bitmap image = BitmapFactory.decodeStream(is);
if (is != null) is.close();
This is useful for the official facebook share, which needs a Bitmap.

Related

Save Screenshot into Internal Storage and Sync into Gallery?

I have searched high and low for a way to do this and the best I could find involved saving the screenshot into the SD Card. What I want to do instead is to onclick(), take a screenshot of the current activity and saves it in the internal storage so that the user can view it in their gallery as and when they want.
Any help is greatly appreciated.
It is harder to post a whole code in here. I think you should follow some tutorials.
According to the your requirement what I got is, You need to take a screenshot using an your application and it should be stored in device SD card.
For that you should add proper permission to the manifest first,
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
and add following code to the activity:
private void takeScreenshot() {
Date now = new Date();
android.text.format.DateFormat.format("yyyy-MM-dd_hh:mm:ss", now);
try {
// image naming and path to include sd card appending name you choose for file, you can change it to your path
String mPath = Environment.getExternalStorageDirectory().toString() + "/" + now + ".jpg";
// create bitmap screen capture
View v1 = getWindow().getDecorView().getRootView();
v1.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
File imageFile = new File(mPath);
FileOutputStream outputStream = new FileOutputStream(imageFile);
int quality = 100;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
outputStream.flush();
outputStream.close();
openScreenshot(imageFile);
} catch (Throwable e) {
e.printStackTrace();
}
}
this code will open the generated image(screenshot):
private void openScreenshot(File imageFile) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(imageFile);
intent.setDataAndType(uri, "image/*");
startActivity(intent);
}
This is how I did for my project. Sometimes this might be not satisfied for you requirement. It it is not satisfied, please follow these tutorials, Thanks
Reference List : http://www.androhub.com/take-a-screenshot-programmatically-in-android/
http://devdeeds.com/take-screenshot-programmatically/
https://www.viralandroid.com/2016/01/how-to-take-screenshot-programmatically-in-android.html
if you want to check whether SD card is available or not. here is the way. If SD card is not available then you can use internal storage to store the image.
Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable();
if(isSDSupportedDevice && isSDPresent)
{
// yes SD-card is present
}
else
{
// SD-card not available
}

Extract exif information of image in android

Im new to android development and trying to get metadata of image using ExifInterface. I stored the image under drawable and trying to get the metadata but getting null values for all fields(date, imagelength, imagewidth). I tried to access image path as this :
String path = "drawable://" + R.drawable.testimage;
and provided this path to ExifInterface.
ExifInterface exif = new ExifInterface(path);
I dont know if storing image under drawable is correct or not because when I run the app in emulator I get something like this :
E/JHEAD﹕ can't open 'drawable://2130837561'
So if this is wrong then please tell me where should I store the image and how to provide image path to ExifInterface.
Thank you in advance.
To get a drawable, you can you this snippet:
Drawable drawable = getResources().getDrawable(android.R.drawable.your_drawable);
I'm not sure if your way is correct, as I've never seen it like that. Do you really need the path to your image to use it on that ExifInterface class?
Ok, I did some digging and found this question, which led me to this one. As it seems, you can not get an absolute path from a resource inside your apk. A good solution would be for you to save it as a file on the external memory, and then you can get the path you want.
First of all, add this to your AndroidManifest.xml, so your app can write to the cellphone memory:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Ok, to save it you can try this, first create a bitmap from your drawable resource:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.your_drawable);
After that get the path you want to save your images, and put it on a String. More info on that here.
The Android docs have a good example on how to get the path. You can see it here.
To keep it simple, I'll copy and paste the snippet from the docs.
void createExternalStoragePrivateFile() {
// Create a path where we will place our private file on external
// storage.
File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
try {
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
InputStream is = getResources().openRawResource(R.drawable.balloons);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
}
}
void deleteExternalStoragePrivateFile() {
// Get path for the file on external storage. If external
// storage is not currently mounted this will fail.
File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
if (file != null) {
file.delete();
}
}
boolean hasExternalStoragePrivateFile() {
// Get path for the file on external storage. If external
// storage is not currently mounted this will fail.
File file = new File(getExternalFilesDir(null), "DemoFile.jpg");
if (file != null) {
return file.exists();
}
return false;
}
After that, get the path of the file you saved on the external memory, and do as you wish.
I'll keep the old example as well. You can use the method getExternalStorageDirectory() to get the path, or getExternalCacheDir(). After that, you can use File method called getAbsolutePath() to get your String.
String path = (...) // (you can choose where to save here.)
File file = new File(path, "your_drawable.png");
FileOutputStream out = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); // You can change the quality from 0 to 100 here, and the format of the file. It can be PNG, JPEG or WEBP.
out.flush();
out.close();
For more info on the Bitmap class, check the docs.
If you need more info, let me know and I'll try to show more samples.
EDIT: I saw your link, and there was this snippet there:
//change with the filename & location of your photo file
String filename = "/sdcard/DSC_3509.JPG";
try {
ExifInterface exif = new ExifInterface(filename);
ShowExif(exif);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(this, "Error!",
Toast.LENGTH_LONG).show();
}
As you can see, if you really want to see the exif data of a internal image resource, you'll have to save it somewhere else, and then you can try to get the absolute path for that File, then, call the method to show the exif.

Android: Capture and store pictures

I'm following this tutorial on taking pictures, displaying thumbnails and storing the full pictures on local public storage available to my application only.
The problem: EACCESS (Permission denied) when trying to access local storage for my application
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ java.io.IOException: open failed: EACCES (Permission denied)
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ at java.io.File.createNewFile(File.java:948)
11-12 10:36:30.765 3746-3746/com.test.example.photo W/System.err﹕ at java.io.File.createTempFile(File.java:1013)
I've looked at this question but it appears to be outdated as none of the solutions work any more today. This question also provides no working solutions. Other results and solutions I've seen and tried seem only vaguely related.
My manifest permissions
</application>
<!-- PERMISSIONS -->
<permission
android:name="android.hardware.Camera.any"
android:required="true" />
<permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:required="true" />
<!-- android:maxSdkVersion="18" seemingly does nothing-->
</manifest>
The method that crashes
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
//THIS IS WHERE IT CRASHES
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
mCurrentPhotoPath = "file:" + image.getAbsolutePath();
return image;
}
I am using an i9250 Galaxy Nexus 3 phone to run the examples, since my emulator doesn't have a camera and automatically GONEs the elements. My target SDK is 16 and I have updated my both my build tools and Android Studio to the latest versions.
I feel like I'm missing something obvious here, since taking pictures is so common in applications and I can't imagine it not working for everyone, but I'm stuck and I'd appreciate your guidance. I am quite new to android, the literature I'm primarily using is Beginning Android 4 Game Programming, Beginning Android 4 and Pro Android 4.
Thank you for your time!
Thanks for the help everyone, it works now!
Apparently I was using the SD card storage which required permissions as explained in permission vs uses-permisson instead of local sandboxed storage which requires no permissions starting from API level 19.
SD card access, requires write permission: Environment.getExternalStoragePublicDirectory
Sandboxed local storage for your app: getExternalFilesDir
I use this code for API level 16, it should require minimal effort to implement and change but if you encounter problems, leave a message and I'll try to help or clarify.
Most of the explanation is in the code as commentary
//OnClick hook, requires implements View.OnClickListener to work
public void takePicture(View v) {
dispatchTakePictureIntent();
}
private void dispatchTakePictureIntent() {
//Create intent to capture an image from the camera
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 directory File where the photo should go, do NOT try to create the image file itself
File photoFile = null;
try {
//mCurrentPhotoPath is a File outside of the methods, so all methods know the last directory for the last picture taken
mCurrentPhotoPath = createImageFile();
photoFile = mCurrentPhotoPath;
} catch (IOException ex) {
// Error occurred while creating the File
ex.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
//photoFile MUST be a directory or the camera will hang on an internal
//error and will refuse to store the picture,
//resulting in not being able to to click accept
//MediaStore will automatically store a jpeg for you in the specific directory and add the filename to the path
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
//unique name, can be pretty much whatever you want
imageId = generateImageId();
//Get file.jpg as bitmap from MediaStore's returned File object
Bitmap imageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath.getAbsolutePath());
//resize it to fit the screen
imageBitmap = Bitmap.createScaledBitmap(imageBitmap,300,300,false);
//Some ImageView in your layout.xml
ImageView imageView = (ImageView)findViewById(R.id.imageView);
imageView.setImageBitmap(imageBitmap);
Bitmap thumbnail = makeThumbnail(mCurrentPhotoPath);
ImageView thumbnail = (ImageView)findViewById(R.id.thumbnail);
thumbnail.setImageBitmap(imageBitmap);
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//completely optional subdirectory structure
storageDir = new File(storageDir, "custom_directory");
return storageDir;
}
private Bitmap makeThumbnail(File currentPhotoPath) {
// Get the dimensions of the View, I strongly recommend creating a <dimens> resource for dip scaled pixels
int targetW = 45;
int targetH = 80;
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(currentPhotoPath.getAbsolutePath(), 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(currentPhotoPath.getAbsolutePath(), bmOptions);
return bitmap;
}
private long generateImageId() {
return Calendar.getInstance().getTimeInMillis();
}
Android 5.0, API 21, will use the Camera2 API where all of this will be hidden far away, from what I understand. You can read about it here
try this:
private File getDir() {
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return new File(sdDir, "Your_photo_dir_here");
}
then:
File pictureFileDir = getDir();
if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {
Log.d("TAG", "Can't create directory to save image.");
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "myphoto_" + date + ".jpg";
String filename = pictureFileDir.getPath() + File.separator + photoFile;
File pictureFile = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (Exception error) {
Log.d("TAG", "File" + filename + "not saved: "
+ error.getMessage());
}
Instead of permission tag use uses-permission
Add this in manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

file.exists() returns false for existing file in android

In my app user can select image from sdcard and set as profile picture. Everything is working fine but when user selects image from whatsapp folder from sdcard image can not decoded.
I am using following code to decode file and display in ImageView.
if (imgFile.exists()) {
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile
.getAbsolutePath());
imgProfilePic.setImageBitmap(myBitmap);
myBitmap = null;
System.gc();
Runtime.getRuntime().gc();
}
I am getting selected image path /storage/sdcard0/WhatsApp/Media/WhatsApp Images/IMG-20130804-WA0000.jpg and it exists in sdcard but file.exists always returns false.
It works fine if user selects image from other folders rather than whatsapp.
Update
I am following steps like
1. click on profilepic(imageview).
2. select options(from camera,galerry,or edit)
3. open selected or captured image in CropImage Activity.
4. display cropped image.
Anyhelp would be greatly appreciated..Thanks.
I was modifying image of other apps. I think this might be the problem. So what I did ?
select image and get path in onActivityResult()
copy image from this path in temp file using below code
use temp file for cropping and other processing
private void copyFile(File sourceFile, File destFile) throws IOException {
if (!sourceFile.exists()) {
return;
}
FileChannel source = null;
FileChannel destination = null;
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
if (destination != null && source != null) {
destination.transferFrom(source, 0, source.size());
}
if (source != null) {
source.close();
}
if (destination != null) {
destination.close();
}
}
Hope this may help someone else.
If your ImageFile.exists() method is giving false in android, but it exists in memory, then you definitely have not given Write-external-storage Permission in the Manifest file of Your Project. Add this Permission in the Manifest of Your Project:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Android Share Intent for a Bitmap - is it possible not to save it prior sharing?

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"))
}
}

Categories

Resources