I have a method that shares bitmap from the app with 3rd party social media apps. I am trying to save my bitmap to the cache folder and share it from there. This is my method:
public void shareMeme(Bitmap bitmap) {
String path = Objects.requireNonNull(getContext()).getCacheDir().getAbsolutePath();
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
Toast.makeText(getContext(), "The Cache drive is: " + path, Toast.LENGTH_LONG).show();
}
The method gets the bitmap from another method through its parameters. I want to know how to incorporate the shareMeme(Bitmap bitmap) parameters with the above code.
UPDATE
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.omar.memegenerator">
...
<application
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.omar.memegenerator.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
...
</application>
</manifest>
The new shareMeme(Bitmap bitmap) method:
public void shareMeme(Bitmap bitmap) {
String path = Objects.requireNonNull(getContext()).getCacheDir().getAbsolutePath();
File file = new File(path + "/Memes/" + timeStamp + counter + ".jpg");
Uri uri = FileProvider.getUriForFile(getContext(), "com.example.omar.memegenerator.fileprovider", file);
try {
OutputStream stream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
stream.flush();
stream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
Toast.makeText(getContext(), "The Cache drive is: " + path, Toast.LENGTH_LONG).show();
}
Stack Trace:
2019-04-09 05:08:51.324 19288-19288/com.example.omar.memegenerator E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.omar.memegenerator, PID: 19288
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.example.omar.memegenerator/cache/Memes/201904090.jpg
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:739)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.example.omar.memegenerator.TopImageFragment.shareMeme(TopImageFragment.java:232)
at com.example.omar.memegenerator.TopImageFragment$6.onReceive(TopImageFragment.java:303)
at android.support.v4.content.LocalBroadcastManager.executePendingBroadcasts(LocalBroadcastManager.java:313)
at android.support.v4.content.LocalBroadcastManager$1.handleMessage(LocalBroadcastManager.java:121)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6703)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)
Step #1: Use compress() on Bitmap to save it to a file in getCacheDir()
Step #2: Add FileProvider to your project, configured to serve files from getCacheDir()
Step #3: Use FileProvider.getUriForFile(), instead of your existing code, to get a Uri to put in EXTRA_STREAM
Step #4: Add Intent.FLAG_GRANT_READ_URI_PERMISSION to the Intent before calling startActivity(), so the recipient has rights to access your content
Related
I'm fetching an image from a URL as a bitmap using glide, and onResourceReady() I'm trying to share the image via intent, but I'm getting an exception. I'm using Android 30.
Glide.with(this)
.asBitmap()
.load("https://i.imgur.com/lkHKgld.jpeg")
.into(new CustomTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, getLocalBitmapUri(resource));
startActivity(Intent.createChooser(i, "Share Image"));
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
My method for providing file URI. Source
public Uri getLocalBitmapUri(Bitmap bmp) {
Uri bmpUri = null;
try {
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "totka_" + System.currentTimeMillis() + ".png");
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;
}
MY Error
2021-02-23 01:07:25.750 19345-19345/com.future_tech.hiravoice E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.future_tech.hiravoice, PID: 19345
com.bumptech.glide.load.engine.CallbackException: Unexpected exception thrown by non-Glide code
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnResourceReady(EngineJob.java:161)
at com.bumptech.glide.load.engine.EngineJob$CallResourceReady.run(EngineJob.java:428)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:264)
at android.app.ActivityThread.main(ActivityThread.java:7581)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
Caused by: android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.future_tech.hiravoice/files/Pictures/totka_1614060445457.png exposed beyond app through ClipData.Item.getUri()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:2083)
at android.net.Uri.checkFileUriExposed(Uri.java:2388)
at android.content.ClipData.prepareToLeaveProcess(ClipData.java:977)
at android.content.Intent.prepareToLeaveProcess(Intent.java:10759)
at android.content.Intent.prepareToLeaveProcess(Intent.java:10765)
at android.content.Intent.prepareToLeaveProcess(Intent.java:10744)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1710)
at android.app.Activity.startActivityForResult(Activity.java:5195)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:675)
at android.app.Activity.startActivityForResult(Activity.java:5153)
at androidx.fragment.app.FragmentActivity.startActivityForResult(FragmentActivity.java:662)
at android.app.Activity.startActivity(Activity.java:5524)
at android.app.Activity.startActivity(Activity.java:5492)
at com.future_tech.hiravoice.activities.AsliTotkayActivity$2.onResourceReady(AsliTotkayActivity.java:185)
at com.future_tech.hiravoice.activities.AsliTotkayActivity$2.onResourceReady(AsliTotkayActivity.java:179)
at com.bumptech.glide.request.SingleRequest.onResourceReady(SingleRequest.java:639)
at com.bumptech.glide.request.SingleRequest.onResourceReady(SingleRequest.java:578)
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnResourceReady(EngineJob.java:159)
at com.bumptech.glide.load.engine.EngineJob$CallResourceReady.run(EngineJob.java:428)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:264)
at android.app.ActivityThread.main(ActivityThread.java:7581)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
I also tried using the IntentShare Library
Glide.with(this)
.asBitmap()
.load("https://i.imgur.com/lkHKgld.jpeg")
.into(new CustomTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
IntentShare.with(getApplicationContext())
.chooserTitle("Share Totka: ")
.text("Default text you would like to share.")
.image(getLocalBitmapUri(resource))
.deliver();
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
But there I was getting an error that content Uri is needed.
com.bumptech.glide.load.engine.CallbackException: Unexpected exception thrown by non-Glide code
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnResourceReady(EngineJob.java:161)
at com.bumptech.glide.load.engine.EngineJob$CallResourceReady.run(EngineJob.java:428)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:264)
at android.app.ActivityThread.main(ActivityThread.java:7581)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
Caused by: java.lang.IllegalArgumentException: Invalid image uri : only content scheme supported : file:///storage/emulated/0/Android/data/com.future_tech.hiravoice/files/Pictures/totka_1614061273090.png
at fr.tvbarthel.intentshare.IntentShare.image(IntentShare.java:229)
at com.future_tech.hiravoice.activities.AsliTotkayActivity$2.onResourceReady(AsliTotkayActivity.java:186)
at com.future_tech.hiravoice.activities.AsliTotkayActivity$2.onResourceReady(AsliTotkayActivity.java:180)
at com.bumptech.glide.request.SingleRequest.onResourceReady(SingleRequest.java:639)
at com.bumptech.glide.request.SingleRequest.onResourceReady(SingleRequest.java:578)
at com.bumptech.glide.load.engine.EngineJob.callCallbackOnResourceReady(EngineJob.java:159)
Well here is how I got it resolved, first add this provider tag in your manifest
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.future_tech.hiravoice.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
</application>
then create a new xml file under res/xml/ named filepaths.xml Source
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--Represents the files in the root directory of the external storage area of the app. The Pictures directory under the Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) directory-->
<!--/storage/emulated/0/Android/data/com.hm.camerademo/files/Pictures-->
<external-files-path name="hm_external_files" path="Pictures" />
</paths>
my method for providing file provider uri
public Uri getLocalBitmapUri(Bitmap bmp) {
Uri bmpUri = null;
try {
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "totka_" + System.currentTimeMillis() + ".png");
FileOutputStream out = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 90, out);
out.close();
bmpUri = FileProvider.getUriForFile(getApplicationContext(), "com.future_tech.hiravoice.fileprovider", file);
} catch (IOException e) {
e.printStackTrace();
}
return bmpUri;
}
finally the glide with Read URI flag set in intent
Glide.with(this)
.asBitmap()
.load("https://i.imgur.com/lkHKgld.jpeg")
.into(new CustomTarget<Bitmap>() {
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, getLocalBitmapUri(resource));
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(i, "Share Image"));
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
I see this file is stored in "private" files of your app (Android/data/<package_name>, any other app can't fetch them strictly without proper permission from your app
try to grant this permission with this line
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, getLocalBitmapUri(resource));
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // granting perm!
startActivity(Intent.createChooser(i, "Share Image"));
from DOC
public static final int FLAG_GRANT_READ_URI_PERMISSION
If set, the recipient of this Intent will be granted permission to perform read operations on the URI in the Intent's data and any URIs specified in its ClipData. When applying to an Intent's ClipData, all URIs as well as recursive traversals through data or other ClipData in Intent items will be granted; only the grant flags of the top-level Intent are used.
and HERE we have a doc about sharing
I'm fairly new into saving media in android, and I'm wondering why this is giving me so much trouble. I am requiring photos taking by my app to be made public, so other apps (including the gallery) can find them.
The documentation states that it if you want an image to be accessible by other apps, you should save them on in the public external storage which is provided by getExternalStoragePublicDirectory(), with the DIRECTORY_PICTURES as the argument.
When I try to do this however, the IOException is thrown in my method, and I'm struggling to find out why.
File creation method:
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
Photo capture method:
public void capturePhoto (View view) {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this, "com.auswide.auswidedashboard", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, IMAGE_REQUEST);
}
}
}
Permissions and Provider code in the Manifest
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.auswide.auswidedashboard"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
And finally the path required by the provider:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.auswide.auswidedashboard/files/Pictures"/>
</paths>
The exception message reads I/System.out: java.io.IOException: Permission denied', which simply confuses me more as I though I gave it the permissions in the manifest?
Any help would be greatly appreciated.
I added a destination file to save my images in app's created folder.
static final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
In cropIwa's documentation, it needs destinationUri
Here is what I have done:
cropIwaView.crop(new CropIwaSaveConfig.Builder(Uri.fromFile(imageRoot.getAbsoluteFile()))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela
cropIwaView.crop(new CropIwaSaveConfig.Builder(getUriFromFile(this, new File(R.xml.file_paths + ".png")))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela a saveCompleteListener of cropiwa.
cropIwaView.setCropSaveCompleteListener(new CropIwaView.CropSaveCompleteListener() {
#Override
public void onCroppedRegionSaved(Uri bitmapUri) {
addPicToGallery(CropProfilePicture.this, bitmapUri);
Toast.makeText(CropProfilePicture.this, "Done", Toast.LENGTH_SHORT).show();
finish();
}
});
Create temp file where the image should save
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile(context);
} catch (IOException ex) {
// Error occurred while creating the File
Timber.e("Can't create photoFile:%s",ex.getMessage());
}
private static File createImageFile(Context ctx) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(
imageFileName, /* prefix */
".png", /* suffix */
storageDir /* directory */
);
}
Generate Uri and use FileProvide when version LOLLIPOP or above
final Uri imageUri ;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
imageUri = Uri.fromFile(photoFile); // file://
else
imageUri = getUriFromFile(context,photoFile); // FileProvider
Saving image logic
cropIwaView.setCropSaveCompleteListener(bitmapUri -> {
addPicToGallery(context, bitmapUri); // sendBroadcast to gallery to scan new added images...
});
cropIwaView.crop(new CropIwaSaveConfig.Builder(imageUri)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
Use FileProvider with android version LOLLIPOP and above
private static Uri getUriFromFile(Context context, File newFile) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", newFile);
}
Under app res add file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.your_package_id/files/Pictures" />
</paths>
manifest.xml
<application
...// other attributes
>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.your_package_id.fileprovider"
android:readPermission="com.your_package_id.fileprovider.READ"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
add permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Update
Update image on gallery.
private static void addPicToGallery(Context context, Uri contentUri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}
I want my users to be able to share an image and select an app to share it to whether its something like their native messenger app, or twitter. Whenever I go to select the app I want to share the image to, I get a message saying "This media can't be loaded" or something like that. Here is the sharing code in BitmapUtils.java
static void shareImage(Context context, String imagePath) {
// Create the share intent and start the share activity
File imageFile = new File(imagePath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri photoURI = FileProvider.getUriForFile(context, FILE_PROVIDER_AUTHORITY, imageFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, photoURI);
context.startActivity(shareIntent);
}
Here is my file provider code in my Manifest file:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.aaronapp.hideme.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
Here is the file_paths file which contains the file paths.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_cache" path="." />
<external-path name="my_images" path="Pictures/" />
</paths>
Here is the share method that is invoked inside the MainActivity class.
/**
* OnClick method for the share button, saves and shares the new bitmap.
*/
#OnClick(R.id.share_button)
public void shareMe() {
// Delete the temporary image file
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
// Share the image
BitmapUtils.shareImage(this, mTempPhotoPath);
}
If you need anymore information that I forgot to show I'll be happy to supply it. I'm trying to fix this issue and get my images to share to different apps(I know Facebook has a certain way of sharing images, but I will tackle that later)
You can also replicate this issue by downloading the Hide me, Emoji App on the google play store, taking a picture and trying to share it across your apps.https://play.google.com/store/apps/details?id=com.aaronapp.hideme
This will allow you to read files from Internal or external sdcard.
Add this in manifest.xml
<!-- this is For Access External file Storage -->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.demo.test.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
<root-path
name="external_files"
path="/storage/"/>
</paths>
Try with adding flag Intent.FLAG_GRANT_READ_URI_PERMISSION to Intent
The file you want to share with another app, you need to allow the client app to access the file. To allow access, grant permissions to the client app by adding the content URI to an Intent and then setting permission flags on the Intent.
// Grant temporary read permission to the content URI
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
The permissions you grant are temporary and expire automatically when the receiving app's task stack is finished.
Please try this code, this is working in my case
File filePath = new File(FIlePath);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
ArrayList<Uri> uriArrayList = new ArrayList<>();
uriArrayList.add(getUriFromFilePath(filePath));
intent.setType("image/*");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList);
startActivity(intent);
public Uri getUriFromFilePath(Context theCtx, File theSrcPath) {
Uri requirdUri = null;
// observation
// SDKversion: 25 -- Uri.fromFile Not working, So we have to use Provider
// FileProvider.getUriForFile will not work when the file is located in external Sdcard.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requirdUri = FileProvider.getUriForFile(theCtx,
theCtx.getApplicationContext().getPackageName() + PROVIDER_FILE_EXTENSION,
theSrcPath);
} else {
requirdUri = Uri.fromFile(theSrcPath);
}
return requirdUri;
}
I have done following changes in your code, i had tested app after
changes and able to share image now.
Please check and let me know.
in MainActivity, shareMe method you have delete the temp file before sharing that why the error was occured.
now i have delete the Temp file after sharing.
Modified code in MainActivity.java
public static final int REQUEST_CODE_SHARE_FILE = 100;
public void shareMe()
{
// BitmapUtils.deleteImageFile(this, mTempPhotoPath); delete temp file in on activity result.
BitmapUtils.shareImage(MainActivity.this, mTempPhotoPath);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
//If the image capture activity was called and was successful
if(requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK)
{
processAndSetImage();
}
else if(requestCode == REQUEST_CODE_SHARE_FILE)
{
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}else {
BitmapUtils.deleteImageFile(this, mTempPhotoPath);
}
}
Modified code in BitmapUtils.java
static void shareImage(Activity activity, String imagePath) {
// Create the share intent and start the share activity
File imageFile = new File(imagePath);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri photoURI = FileProvider.getUriForFile(activity, FILE_PROVIDER_AUTHORITY, imageFile);
shareIntent.putExtra(Intent.EXTRA_STREAM, photoURI);
activity.startActivityForResult(shareIntent,MainActivity.REQUEST_CODE_SHARE_FILE);
}
I am testing your app on the device with Android version 6.0 and app gets crashed every time.
Inside detectFacesandOverlayEmoji method, on line number 32(SparseArray faces = detector.detect(frame);)
a) Sometimes app remains open without showing anything in logcat.
b) Sometimes app crashed with following error in logcat.
05-24 11:00:27.192 17880-17880/com.aaronapp.hideme E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.aaronapp.hideme, PID: 17880
java.lang.OutOfMemoryError: Failed to allocate a 51916812 byte allocation with 16765168 free bytes and 36MB until OOM
at com.google.android.gms.vision.Frame.zzTM(Unknown Source)
at com.google.android.gms.vision.Frame.getGrayscaleImageData(Unknown Source)
at com.google.android.gms.vision.face.FaceDetector.detect(Unknown Source)
at com.aaronapp.hideme.HideMe.detectFacesandOverlayEmoji(HideMe.java:32)
at com.aaronapp.hideme.MainActivity.processAndSetImage(MainActivity.java:153)
at com.aaronapp.hideme.MainActivity.onActivityResult(MainActivity.java:133)
at android.app.Activity.dispatchActivityResult(Activity.java:6428)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742)
at android.app.ActivityThread.-wrap16(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Make use of Picasso, sharing you a working code from my project.
Put this in your App level gradle,
implementation 'com.squareup.picasso:picasso:2.71828'
Code used is given below,
static void shareImage(Context context, String imagePath) {
// Create the share intent and start the share activity
Picasso.with(getApplicationContext())
.load(imagePath)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("image/*");
i.putExtra(Intent.EXTRA_STREAM, getLocalBitmapUri(bitmap));
if (!TextUtils.isEmpty(des)) {
i.putExtra(Intent.EXTRA_TEXT, des);
}
startActivity(Intent.createChooser(i, "Share Image"));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
}
public Uri getLocalBitmapUri(Bitmap bmp) {
Uri bmpUri = null;
try {
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES),
"share_image_" + System.currentTimeMillis() + ".png");
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;
}
Hope it may help you.
I have seen this question asked previously, but before you mark this question as redundant let me just say that I've been trying to figure this out for three hours and I'm just confused. My goal is to take a photo, set it to an imageview, and upload it to Firebase Storage. In order to get the full size photo, I believe I have to save it to a local file, because the FileProvider takes a reference to a file and that's where the full size photo goes. Is that correct so far? I cannot for the life of me get the FileProvider to accomplish this.
After many attempts, this is what I have:
// It all starts here
public void onClick(View v) {
Log.d("DebugMySocks", "Change photo button clicked");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getApplicationContext(),
getPackageName(),
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp;
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mProfileImage.setImageBitmap(imageBitmap);
}
}
// in manifest file
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
// in /xml/filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
</paths>
and the error message is:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.kevin.moresocksplease/files/Pictures/JPEG_20171214_111337.jpg706457555.jpg
Replace:
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
with:
<external-files-path name="my_images" />