Add text into Image and share it as Image - android

I need some help here.
I want to add a new feature to my app. I have a page that displays some text and I have a share button with it. Now the user can share this text with anyone, So what I want is that the user can share this text as an Image, So Instead of sharing this just like a text, I will add this new feature to also can share it as an Image.
Now I already created the Image that will include this text to be shared,
What I need to do is, When the user clicks on the share button, it is should send this text to the Image a Put it into it "It will be empty and the text will be like drawing on it. And after the text adds to the Image, The user can send this image with the text.
I made some searches on Google and I found some people who say you can add Textview into the ImageView and send this text with Intent then display the text in the Textview and then share it. But of course, this will not work because the text didn't actually add to the Image and It is just a view, so the Image will share as it is "Empty".
Any suggestions for how can I make something like that?

Wrap your image and text into a relative layout or constraint layout or a frame layout, take a screenshot of that relative or constraint or frame layout, and share it, use This Library to take a screen shot of your view.
here is some code for you
Bitmap bitmap = ScreenShott.getInstance().takeScreenShotOfJustView(yourview);
File sharedFile = FileUtility.shareImageFile(bitmap);
if (sharedFile != null) {
Uri uri = FileProvider.getUriForFile(context, context.getPackageName().concat(".provider"), sharedFile);
Intent intent = new ShareCompat.IntentBuilder(context)
.setType(context.getContentResolver().getType(uri))
.setStream(uri)
.getIntent();
intent.setAction(Intent.ACTION_SEND);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "share"));
}
FileUtility shareImageFile method
public static File shareImageFile(Bitmap bitmap) {
String rootDirectory = ResourceProvider.get().getContext().getExternalCacheDir() + File.separator + Environment.DIRECTORY_PICTURES + File.separator;
File rootFile = new File(rootDirectory);
if (!rootFile.exists()) {
boolean make = rootFile.mkdirs();
Log.d(TAG, "shareImageFileMakeStatus: " + make);
}
String imagePath = rootDirectory.concat("image_").concat(currentDate()).concat(".png");
try {
FileOutputStream fos = new FileOutputStream(imagePath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return new File(imagePath);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
I use external cashe dir because there is no need to get storage permission from user for this path.
file provider that you must add xml folder in your res and create a .xml file for file provider
res/xml/provide_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
add this provider in your manifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>

You can make use of DrawingCache. Put your image and text inside a wrapper like FrameLayout/RelativeLayout/etc and use its drawing cache to save it as a bitmap;
View view = findViewById(R.id.flWrapper);
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(flUser.getDrawingCache());
view.setDrawingCacheEnabled(false);
// if you want to save it to a file
File image = new File("/path/to/your/file.jpg");
try (FileOutputStream outputStream = new FileOutputStream(image)) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
// handle it
} catch (IOException e) {
e.printStackTrace();
// handle it
}
// or share the bitmap as you like

There are 3 steps to this .
Create the view you want to send
Convert the view to Bitmap
Send it via an intent after saving it in your app or use it for your imageview
Create the view you wanto to send
So create a constraint layout which has that textview which is populated by the text you want to give it from the previous activity via a `intent.getExtra("text")
Have the images and other data in the *same constraint layout which you want to send. Once yo're satisfied with how the view/viewgroup aka the constraint layout looks. we are ready to convert that to bitmap
2.Convert the viewgroup to Bitmap
v.measure(MeasureSpec.makeMeasureSpec(v.getLayoutParams().width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(v.getLayoutParams().height, MeasureSpec.EXACTLY));
v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.RGB_565);
Canvas c = new Canvas(b);
v.draw(c);
where v is the viewgroup you want to convert to bitmap
Viewgroup to Bitmap Stackoverflow
3.Send that bitmap as an intent
String pathofBmp = Images.Media.insertImage(getContentResolver(), bitmap,"title", null);
Uri bmpUri = Uri.parse(pathofBmp);
final Intent emailIntent1 = new Intent( android.content.Intent.ACTION_SEND);
emailIntent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent1.putExtra(Intent.EXTRA_STREAM, bmpUri);
emailIntent1.setType("image/png");
and for imageview it's as simple as
imageview.setSrc(bitmap)
Share Bitmap via Android Stackoverllow answer

Related

How to share and save the ImageView from First Activity to Third Activity

Activity 1:I have an imageview in which the images taken from camera and gallery are set and it works fine. In this Activity there is a right click button which will redirect you to second activity.
Activity 2: In this Activity I have four options
Save
Share
Share Via Instagram
Share Via Facebook
Activity 3: From the above four options the third activity works accordingly.
Now I don't know how to pass the image taken in first activity to the third activity.
My Effort: In first Activity image taken from camera:
Intent i=new Intent(Camera.this,SaveVia.class);
i.putExtra("image", thumbnail );
startActivity(i);
In Second Activity SaveVia:
Intent intent2 = new Intent(SaveVia.this, Save.class);
Bitmap receiptimage = (Bitmap)getIntent().getExtras().getParcelable("image")
startActivity(intent2);
In third Activity called Save:
Bitmap receiptimage = (Bitmap) getIntent().getExtras().getParcelable("imagepass");
// receipt.setImageBitmap(receiptimage);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
receiptimage.compress(Bitmap.CompressFormat.PNG, 90, bytes);
File storagePath = new File(Environment.getExternalStorageDirectory() + "/PhotoAR/");
storagePath.mkdirs();
File destination = new File(storagePath, Long.toString(System.currentTimeMillis()) + ".jpg");
FileOutputStream fo;
try {
destination.createNewFile();
fo = new FileOutputStream(destination);
fo.write(bytes.toByteArray());
fo.close();
Toast.makeText(Save.this,"No Error",Toast.LENGTH_LONG).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(Save.this,"Error Arrived",Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(Save.this,"Error Arrived again",Toast.LENGTH_LONG).show();
#AND
You don't need to pass bitmap as Parcelable from one activity.Pass file path in intent from one activity to another.
When you capture image get path in onActivityresult and save that path.
And redirect that two second activity.And from that path show your bitmap.Just Share Uri from path in share on facebook and Instagram.
I recommend using File I/O to save the bitmap to disk. Put the path of the file into your intent bundle using putExtra and recreate the bitmap in your other screens. Putting large bitmaps into a bundle with an intent can cause TransactionTooLarge exceptions.
Take a look at Getting path of captured image in android using camera intent

FileProvider path creation and BitmapFactory.decodefile problems in 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.

Exporting Subview of Fragment from another Fragment as Png

How do i export a subview of a fragment from another fragment as Png?
Context
I'am creating an app, which allows the user to create a personalized CV. The user can submit informations regarding his job-experience and skills. As a result the user is able to export his results as a png and save it to the device. My question aims at the export-functionallity of the application.
What i got so far
I tried to combine several answers from the site to get a result, but unfortunately the code that i have does not work so far.
public void export(Context context) throws FileNotFoundException {
View exportView = getLayoutInflater(getArguments()).inflate(R.layout.fragment_form, null, false);
RelativeLayout subView = (RelativeLayout) exportView.findViewById(R.id.fragment_form_container_root);
try {
subView.setDrawingCacheEnabled(true);
subView.measure(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
Bitmap b = Bitmap.createBitmap(subView.getDrawingCache(), 0, 0, subView.getMeasuredWidth(), subView.getMeasuredHeight());
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
b.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
File imagePath = new File(context.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(context, "com.example.lukas.masterthesis.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, getActivity().getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
}
The export functionallity is called within Fragment A after a click event. I'am trying to get the wanted subview of Fragment B (wich seems to work), but creating the Bitmap always results in a null Object. Therefore the procedure fails.
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat, int, java.io.OutputStream)' on a null
Honestly im not even sure this is the proper way to do such a thing, since I haven't found a Best-Practice solution for what im trying. If not, does anybody know a better way to do this?
If its an "ok" solution, does anybody know how to get my code to work or has some suggestions that can point me into the right direction?
Thx in advance.
try using
try using bitmap = Bitmap.createBitmap(subView.getDrawingCache());
instead of
Bitmap b = subView.getDrawingCache();
Just wanted to provide an answer.
I was basically on the right track. Ended up splitting my View into smaller parts since may drawing cache was to small to handle the whole thing at once. It might help somebody who has encountered a similar problem.
First part of code draws each part into the resulting bitmap. Second part allows the user to exports the it (for example to dropbox).
So here you go
public void export() throws FileNotFoundException {
View exportView = rootView.findViewById(R.id.fragment_form_container_root);
View view_title = rootView.findViewById(R.id.fragment_form_container_title);
View view_basicInformation = rootView.findViewById(R.id.fragment_form_container_basicinformation);
View view_experience_skill = rootView.findViewById(R.id.fragment_form_container_experience_skill);
View view_connect = rootView.findViewById(R.id.fragment_form_container_connect);
View view_footer = rootView.findViewById(R.id.fragment_form_container_footer);
try {
view_title.setDrawingCacheEnabled(true);
view_title.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b0 = Bitmap.createBitmap(view_title.getDrawingCache());
view_title.setDrawingCacheEnabled(false);
view_basicInformation.setDrawingCacheEnabled(true);
view_basicInformation.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b1 = Bitmap.createBitmap(view_basicInformation.getDrawingCache());
view_basicInformation.setDrawingCacheEnabled(false);
view_experience_skill.setDrawingCacheEnabled(true);
view_experience_skill.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b2 = Bitmap.createBitmap(view_experience_skill.getDrawingCache());
view_experience_skill.setDrawingCacheEnabled(false);
view_connect.setDrawingCacheEnabled(true);
view_connect.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b3 = Bitmap.createBitmap(view_connect.getDrawingCache());
view_connect.setDrawingCacheEnabled(false);
view_footer.setDrawingCacheEnabled(true);
view_footer.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
Bitmap b4 = Bitmap.createBitmap(view_footer.getDrawingCache());
view_footer.setDrawingCacheEnabled(false);
Bitmap result = Bitmap.createBitmap(exportView.getWidth(), exportView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
//canvas.setDensity(300);
Paint paint = new Paint();
paint.setFlags(Paint.FILTER_BITMAP_FLAG);
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
canvas.drawBitmap(b0, 0, 0, paint);
canvas.drawBitmap(b1, 0, b0.getHeight(), paint);
canvas.drawBitmap(b2, 0, b0.getHeight() + b1.getHeight(), paint);
canvas.drawBitmap(b3, 0, b0.getHeight() + b1.getHeight() + b2.getHeight(), paint);
canvas.drawBitmap(b4, 0, b0.getHeight() + b1.getHeight() + b2.getHeight() + b3.getHeight(), paint);
File cachePath = new File(string_CachePath, "images"); // -> string_CachePath == context.getCacheDir();
cachePath.mkdirs(); // don't forget to make the directory
FileOutputStream stream = new FileOutputStream(cachePath + "/image.png"); // overwrites this image every time
result.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
File newFile = new File(cachePath.getPath(), "image.png");
Uri contentUri = FileProvider.getUriForFile(getActivity(), "com.example.lukas.masterthesis.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, getActivity().getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
} catch (Exception e) {
e.printStackTrace();
}
}

Share image with ShareActionProvider from Picasso

i spand few hours to find this solution...
so i decided to share this informatiom, maybe some one itwill helpful :)
The first way, shown below, takes the bitmap from the view and loads it into a file.
// Get access to ImageView
ImageView ivImage = (ImageView) findViewById(R.id.ivResult);
// Fire async request to load image
Picasso.with(context).load(imageUrl).into(ivImage);
and then later assuming after the image has completed loading, this is how you can trigger a share:
// Can be triggered by a view event such as a button press
public void onShareItem(View v) {
// Get access to bitmap image from view
ImageView ivImage = (ImageView) findViewById(R.id.ivResult);
// Get access to the URI for the bitmap
Uri bmpUri = getLocalBitmapUri(ivImage);
if (bmpUri != null) {
// Construct a ShareIntent with link to image
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, bmpUri);
shareIntent.setType("image/*");
// Launch sharing dialog for image
startActivity(Intent.createChooser(shareIntent, "Share Image"));
} else {
// ...sharing failed, handle error
}
}
// Returns the URI path to the Bitmap displayed in specified ImageView
public Uri getLocalBitmapUri(ImageView imageView) {
// Extract Bitmap from ImageView drawable
Drawable drawable = imageView.getDrawable();
Bitmap bmp = null;
if (drawable instanceof BitmapDrawable){
bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
} else {
return null;
}
// Store image to default external storage directory
Uri bmpUri = null;
try {
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "share_image_" + System.currentTimeMillis() + ".png");
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = Uri.fromFile(file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
Make sure to add the appropriate permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
The second way to share an Image does not require you to write the image into a file. This code can safely be executed on the UI thread. The approach was suggested on this webpage http://www.nurne.com/2012/07/android-how-to-attach-image-file-from.html .
ImageView siv = (ImageView) findViewById(R.id.ivResult);
Drawable mDrawable = siv.getDrawable();
Bitmap mBitmap = ((BitmapDrawable)mDrawable).getBitmap();
String path = Images.Media.insertImage(getContentResolver(),
mBitmap, "Image Description", null);
Uri uri = Uri.parse(path);
return uri;
You get the Drawable from the ImageView. You get the Bitmap from the Drawable. Put that bitmap into the Media image store. That gives you a path which can be used instead of a file path or URL. Note the original webpage had an additional problem with immutable bitmaps, solved by drawing the bitmap into a canvas (never shown on screen). See linked page above for details.

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