Android Application Crashes while Capturing Full Size Picture - java

I am developing an Android Application that takes a picture and saves a full-size picture onto the device. I have mostly taken help from developers android site. When I run my code and try to take the picture the application crashes instantly. The code I am using is as follows:
static final int REQUEST_IMAGE_CAPTURE = 1;
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
System.out.println("Error in Dispatch Take Picture Intent");
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
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");
//imageView.setImageBitmap(imageBitmap);
Toast.makeText(getApplicationContext(),"Working",Toast.LENGTH_LONG).show();
}
}
String currentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
private View.OnClickListener onCamClick() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("Starting Camera Activity");
//dispatchTakePictureIntent();
//Toast.makeText(getApplicationContext(),"Working",Toast.LENGTH_LONG).show();
dispatchTakePictureIntent();
}
};
}
When I run my code I get the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.zaeem.tia, PID: 15468
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.zaeem.tia/files/Pictures/JPEG_20200301_003227_1043176843378455577.jpg
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:739)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.zaeem.tia.app.activities.MainActivity.dispatchTakePictureIntent(MainActivity.java:121)
at com.zaeem.tia.app.activities.MainActivity.access$000(MainActivity.java:40)
at com.zaeem.tia.app.activities.MainActivity$1.onClick(MainActivity.java:164)
at android.view.View.performClick(View.java:7339)
at android.widget.TextView.performClick(TextView.java:14221)
at android.view.View.performClickInternal(View.java:7305)
at android.view.View.access$3200(View.java:846)
at android.view.View$PerformClick.run(View.java:27787)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7076)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
filepath.xml file:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Android/data/com.zaeem.tia.app.activities/files/Pictures" />
</paths>
Please advise regarding this matter.

Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
Here, you are saying that the authority string for your FileProvider is com.example.android.fileprovider. Based on the error, that is not what you have on the <provider> element in the manifest.

add this in your manifest.xml (after ):
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="android.getqardio.com.gmslocationtest"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
Create provider_paths on Resources/xml, and add this content:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
Activity:
File imagePath = new File(getFilesDir(), "external_files");
imagePath.mkdir();
File imageFile = new File(imagePath.getPath(), "test.jpg");
// Write data in your file
Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);
Intent intent = ShareCompat.IntentBuilder.from(this)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.setAction(Intent.ACTION_VIEW) //Change if needed
.setDataAndType(uri, "image/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

Related

Can't install apk from uri on Android 10 in Eclipse projects

We are installing apk from file uri, its working upto Android Pie(9) ,in Android 10 mobile its showing "There was problem while Parsing". we have stored file in the application storage only and the build version is 4.4
I have shared the code below.
String PATH = Objects.requireNonNull(this.myActivity.getExternalFilesDir(null)).getAbsolutePath();
File file = new File(PATH + "/"+Utils.apk_name);
intent = new Intent("android.intent.action.VIEW");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(this.myActivity, this.myActivity.getApplicationContext().getPackageName() + ".provider", file);
//Intrinsics.checkExpressionValueIsNotNull(uri, "FileProvider.getUriForFi…eProvider.install\", file)");
intent.setDataAndType(uri, "application/vnd.android.package-archive");
startActivity(intent);
Provider in Manifest
<provider
android:name="com.kirubha.helpers.ESEProvider"
android:authorities="com.kirubha.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
provider_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="."/>
</paths>
I have found most of my file provider worries disapear when I add
StrictMode.VmPolicy.Builder buildervm = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(buildervm.build());
to my main activities.
The above was found in this answer which might be useful to you.
android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
In Android 10, ACTION_VIEW is deprecated, need to use PackageInstaller instead.
Follow the below code to implement PackageInstaller in Java.
private static int REQUEST_OEN_DOCUMENT = 1337;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent1 = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent1.setType("application/vnd.android.package-archive");
intent1.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent1, REQUEST_OPEN_DOCUMENT);
}
This code will redirect to the files folder. Then need to select the .apk file.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_OEN_DOCUMENT) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
Uri uri = data.getData();
installAfterQ(uri);
}
}
}
}
public void installAfterQ(Uri uri) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
InputStream inputStream = getApplication().getContentResolver().openInputStream(uri);
long length = DocumentFile.fromSingleUri(getApplication(), uri).length();
PackageInstaller installer = getApplication().getPackageManager().getPackageInstaller();
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
int sessionId = installer.createSession(params);
PackageInstaller.Session session = installer.openSession(sessionId);
File file = new File(uri.getPath());
OutputStream outputStream = session.openWrite(file.getName(), 0, length);
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, (int) len);
}
session.fsync(outputStream);
outputStream.close();
inputStream.close();
Intent intent = new Intent(getApplication(), UpdateReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(getApplication(), 3439, intent, PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pi.getIntentSender());
session.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
Update Receiver Class:
public class UpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1);
if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
Intent activityIntent =intent.getParcelableExtra(Intent.EXTRA_INTENT);
context.startActivity(activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else if (status == PackageInstaller.STATUS_SUCCESS) {
Toast.makeText(context, "Successfully Installed", Toast.LENGTH_SHORT).show();
} else {
String msg =intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
Toast.makeText(context, "Error while installing" + msg, Toast.LENGTH_SHORT).show();
}
}
}
Android Manifest.xml
Inside Application tag.
<receiver android:name=".UpdateReceiver"/>
<meta-data
android:name="com.android.packageinstaller.notification.smallIcon"
android:resource="#drawable/ic_install_notification" />
Permission:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Try this code. Hope this code is helpful.

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/

EDIT:Ok I have tried the suggestions and changed it to getExternalFilesDir() and am still getting the same error. Skip to the bottom where it says "EDITED CODE" to see what it is now. I also changed it so that the screenshot will save to the pictures directory instead of creating a new directory. (END EDIT)
I have an android app that contains a recyclerview. I have created a button that will export create a PNG of the recyclerview data, save it to the device, and then send it as an attachment to the email application so it can be emailed. I'm getting the exception " java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/ExportedFlares/FlareData.png"
Here is the code for the function that saves the bitmap to the device:
private void saveBitmap(Bitmap bitmap){
if(bitmap!=null){
try {
FileOutputStream outputStream = null;
try {
String file_path = Environment.getExternalStorageDirectory().getAbsolutePath() +
"/ExportedFlares";
File dir = new File(file_path, "FlareData");
if(!dir.exists())
dir.mkdirs();
outputStream = new FileOutputStream(dir); //here is set your file path where you want to save or also here you can set file object directly
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); // bitmap is your Bitmap instance, if you want to compress it you can compress reduce percentage
// PNG is a lossless format, the compression factor (100) is ignored
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
And here is the onClick code for when the button is tapped:
public void onClick(View v) {
saveBitmap(getScreenshotFromRecyclerView(recyclerView));
String filename = "FlareData.png";
File fileLocation = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/ExportedFlares", filename);
Uri path = FileProvider.getUriForFile(FlareActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileLocation);
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent .setType("vnd.android.cursor.dir/email");
String to[] = {"email#gmail.com"};
emailIntent .putExtra(Intent.EXTRA_EMAIL, to);
emailIntent .putExtra(Intent.EXTRA_STREAM, path);
emailIntent .putExtra(Intent.EXTRA_SUBJECT, "Subject");
startActivity(Intent.createChooser(emailIntent , "Send email..."));
The final line of the following code is what is throwing the exception:
File fileLocation = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/ExportedFlares", filename);
Uri path = FileProvider.getUriForFile(FlareActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileLocation);
Here is the XML data, I have provider_paths.xml here:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="FlareData.png" path="ExportedFlares/"/>
</paths>
And this is from the manifest:
enter code here
<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>
EDITED CODE:
emailFlaresButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = getApplicationContext();
saveBitmap(context, getScreenshotFromRecyclerView(recyclerView));
String filename = "FlareData.png";
File fileLocation = new File(context.getExternalFilesDir(DIRECTORY_PICTURES).getAbsolutePath()
, filename);
Uri path = FileProvider.getUriForFile(FlareActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileLocation);
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent .setType("vnd.android.cursor.dir/email");
String to[] = {"asd#gmail.com"};
emailIntent .putExtra(Intent.EXTRA_EMAIL, to);
emailIntent .putExtra(Intent.EXTRA_STREAM, path);
emailIntent .putExtra(Intent.EXTRA_SUBJECT, "Subject");
startActivity(Intent.createChooser(emailIntent , "Send
email..."));
// Intent intent = new Intent(FlareActivity.this,
AddFlareActivity.class);
//startActivityForResult(intent, ADD_FLARE_RESULT_CODE);
}
});
And here is the piece of code that the error is pointing to (the last line) :
Uri path = FileProvider.getUriForFile(FlareActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileLocation);
I have tried it with the provider path being set to both external-files-path and external-path and it doesn't affect the issue
EDIT3: Full stack trace:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.fibrnah, PID: 22052
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.android.fibrnah/files/Pictures/FlareData.png
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:739)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.android.fibrnah.FlareActivity$2.onClick(FlareActivity.java:84)
at android.view.View.performClick(View.java:6274)
at android.view.View$PerformClick.run(View.java:24859)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6710)
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:770)
Change external-files-path to external-path.
Or, put your files in getExternalFilesDir() (called on a Context). This is the better solution, as your current code will not work on Android Q (by default) and Android R+ (for all apps).

Picking an image from gallery gives error Android N

I am having trouble with retrieving the Uri from an intent in Android N.
As far as I know on Android 24 and above to get an external Uri you need FileProvider declared in Manifest. That's all done and it works with the camera, but when I try to get an image from the gallery I get an error in onActivityResult data.getData();
These are a few samples of my code:
public void getPictureFromGallery(){
picUriCar = null;
try {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, PIC_SELECT);
} catch (ActivityNotFoundException e) {
Toast.makeText(MainActivity.this, getResources().getString(R.string.text_error_no_gallery_app),
Toast.LENGTH_SHORT).show();
}
}
And onActivityResult:
else if(requestCode == PIC_SELECT){
picUriCar = data.getData();
if (picUriCar != null){
performCrop();
}
}
As far as I know data.getData() returns a Uri and this works ok on Marshmallow, but on a Nougat phone i get this error:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=5, result=-1, data=Intent {
dat=content://com.android.externalstorage.documents/document/4996-1EFF:DCIM/100ANDRO/DSC_0004.JPG
flg=0x1 }} to activity
{com.company.example/com.company.example.MainActivity}:
java.lang.SecurityException: Uid 10246 does not have permission to uri
0 #
content://com.android.externalstorage.documents/document/4996-1EFF%3ADCIM%2F100ANDRO%2FDSC_0004.JPG
at android.app.ActivityThread.deliverResults(ActivityThread.java:4267)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4310)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1628)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6361)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
Caused by: java.lang.SecurityException: Uid 10246 does not have permission to uri 0 #
content://com.android.externalstorage.documents/document/4996-1EFF%3ADCIM%2F100ANDRO%2FDSC_0004.JPG
at android.os.Parcel.readException(Parcel.java:1683)
at android.os.Parcel.readException(Parcel.java:1636)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:3213)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1525)
at android.app.Activity.startActivityForResult(Activity.java:4235)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:767)
at android.app.Activity.startActivityForResult(Activity.java:4194)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:754)
at com.company.example.MainActivity.performCrop(MainActivity.java:1654)
at com.company.example.MainActivity.onActivityResult(MainActivity.java:1534)
at android.app.Activity.dispatchActivityResult(Activity.java:6928)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4263)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4310) 
at android.app.ActivityThread.-wrap20(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1628) 
at android.os.Handler.dispatchMessage(Handler.java:110) 
at android.os.Looper.loop(Looper.java:203) 
at android.app.ActivityThread.main(ActivityThread.java:6361) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
My question is:
How do I pass data.getData() uri to picUriCar without any errors?
Use this code to create new intent to choose image:
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType(image/*);
startActivityForResult(Intent.createChooser(intent, ""), Constants.SELECT_PICTURE);
Use this code in onActivityResult:
if (resultCode == Activity.RESULT_OK) {
if (requestCode == Constants.SELECT_PICTURE) {
Uri selectedImageUri = data.getData();
try {
if (isNewGooglePhotosUri(selectedImageUri)) {
resultFile = getPhotoFile(selectedImageUri);
} else {
resultFile = getFilePathForGallery(selectedImageUri);
}
if (resultFile == null) {
//error
return;
}
} catch (Exception e) {
e.printStackTrace();
//error
return;
}
}
}
}
Also here is some usefull function that i use in my code:
private File getFilePathForGallery(Uri contentUri) {
String path = null;
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
if (cursor.moveToFirst()) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
path = cursor.getString(column_index);
}
cursor.close();
return new File(path);
}
public static boolean isNewGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
}
private File getPhotoFile(Uri selectedImageUri) {
try {
InputStream is = mActivityInstance.getContentResolver().openInputStream(selectedImageUri);
if (is != null) {
Bitmap pictureBitmap = BitmapFactory.decodeStream(is);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
pictureBitmap.compress(Bitmap.CompressFormat.JPEG, 80, bytes);
File output = new File(FileManager.getImageCacheDir(mActivityInstance), System.currentTimeMillis() + ".jpg");
output.createNewFile();
FileOutputStream fo = new FileOutputStream(output);
fo.write(bytes.toByteArray());
fo.close();
return output;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Functions from FileManager class:
private static File getCacheDir(Context context) {
File cacheDir = context.getExternalFilesDir(null);
if (cacheDir != null) {
if (!cacheDir.exists())
cacheDir.mkdirs();
} else {
cacheDir = context.getCacheDir();
}
return cacheDir;
}
public static File getImageCacheDir(Context context) {
File imageCacheDir = new File(getCacheDir(context), "cache_folder");
if (!imageCacheDir.exists())
imageCacheDir.mkdirs();
return imageCacheDir;
}
Also you need to create new xml file in your xml folder:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
and then add new provider to manifest file:
<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/your_xml_file" />
</provider>
I did a few checks and as always the solution was simpler that I expected. This is my performCrop function. I have two options when clicking on a View. Either get the image from the gallery or capture it with the camera. I remembered that the gallery option problem occurred only after I fixed the camera problem. So the issue might be somewhere within the crop code. So this is what I did:
public void performCrop(boolean cameraShot){
try {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
//indicate image type and Uri
//cropIntent.setDataAndType(picUri, "image");
cropIntent.setDataAndType(picUriCar, "image/*");
//set crop properties
cropIntent.putExtra("crop", "true");
//indicate aspect of desired crop
cropIntent.putExtra("aspectX", 2);
cropIntent.putExtra("aspectY", 1);
//indicate output X and Y
cropIntent.putExtra("outputX", 1024);
cropIntent.putExtra("outputY", 512);
cropIntent.putExtra("scale", true);
cropIntent.putExtra("return-data", false);
Uri uri;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !cameraShot) {
uri = Uri.fromFile(croppedFileCar);
}else{
String authority = "com.company.example.provider";
uri = FileProvider.getUriForFile(
MainActivity.this,
authority,
croppedFileCar);
cropIntent.setClipData(ClipData.newRawUri( "", uri ) );
cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
Please notice - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
!cameraShot)
So basically the logic behind it is that if the image is not provided by the camera (which is determined in onActivityResult) or API is below 24, execute the line:
uri = Uri.fromFile(croppedFileCar);
Otherwise use the FileProvider. Before I did these changes the app crashed when picking an image from the gallery. I have no idea why it works now, but if someone knows the answer, I would be happy to hear it.

Save downloaded file in public Downloads directory and open it with intent

I am having trouble downloading and opening a file.
I successfully download the file in device's public external Download folder and I can open it through File Explorer, but when I want to open it with intent right after the download has finished from my app, java.lang.IllegalArgumentException occurs:
Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /file:/storage/emulated/0/Download/Colors.pdf
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:719)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:404)
at com.example.alla.bitchface.MainActivity$1.onReceive(MainActivity.java:97)
at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:1122)
at android.os.Handler.handleCallback(Handler.java:751) 
at android.os.Handler.dispatchMessage(Handler.java:95) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6121) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) 
MainActivity.java:97 is
Uri uri = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID+".provider",
new File(filePath));
I tried to implement FileProvider, but I can't figure out how to make it work with public directories. Also when I do the "hack" with removing StrictMode, the file opens. Here's my code:
#Override
public void onDownloadPrepared(Request downloadRequest, String fileName) {
downloadRequest.allowScanningByMediaScanner();
downloadRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,
fileName);
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
if (manager == null) return;
long id = manager.enqueue(downloadRequest);
BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long receivedId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,
-1);
if (id == receivedId) {
Query query = new Query();
query.setFilterById(id);
Cursor cursor = manager.query(query);
if (cursor.moveToFirst()) {
int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) {
String filePath = cursor.getString(
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
Intent intent1 = new Intent(Intent.ACTION_VIEW);
Uri uri = FileProvider.getUriForFile(MainActivity.this,
BuildConfig.APPLICATION_ID+".provider",
new File(filePath));
intent1.setData(uri);
intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent1);
}
}
unregisterReceiver(this);
}
}
};
IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
registerReceiver(downloadReceiver, filter);
}
<paths>
<external-path name="external_files" path="."/>
</paths>
<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>
Remove the "file" prefix from the path:
filePath = filePath.replace("file:/","");
Posting this as a Community Wiki Answer instead of it being edited into the Question.
The Download folder is not listed as a path, supported by your FileProvider.
Try adding the following to the provider_path.xml:

Android sharing Files, by sending them via email or other apps

I have a list of files in my android app and I want to be able to get the selected items and send them via email or any other sharing app. Here is my code.
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_EMAIL, getListView().getCheckedItemIds());
sendIntent.setType("text/plain");
startActivity(sendIntent);
this is the code for sharing file in android
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(myFilePath);
if(fileWithinMyDir.exists()) {
intentShareFile.setType("application/pdf");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+myFilePath));
intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
"Sharing File...");
intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
Below is another method to share PDF file if you have put file provider in manifest
Uri pdfUri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pdfUri = FileProvider.getUriForFile(PDFViewerActivity.this, fileprovider, pdfFile);
} else {
pdfUri = Uri.fromFile(pdfFile);
}
Intent share = new Intent();
share.setAction(Intent.ACTION_SEND);
share.setType("application/pdf");
share.putExtra(Intent.EXTRA_STREAM, pdfUri);
startActivity(Intent.createChooser(share, "Share file"));
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(exportPath));
also you can make zip file of all file and attach zip file for send multiple file in android
For those who trying in Kotlin here is the way:
Start the intent like below:
fun startFileShareIntent(filePath: String) { // pass the file path where the actual file is located.
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = FILE_TYPE // "*/*" will accepts all types of files, if you want specific then change it on your need.
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
putExtra(
Intent.EXTRA_SUBJECT,
"Sharing file from the AppName"
)
putExtra(
Intent.EXTRA_TEXT,
"Sharing file from the AppName with some description"
)
val fileURI = FileProvider.getUriForFile(
context!!, context!!.packageName + ".provider",
File(filePath)
)
putExtra(Intent.EXTRA_STREAM, fileURI)
}
startActivity(shareIntent)
}
In Manifest inside the application tag:
<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>
Under res-->xml--> provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="files" path="." />
<external-path name="external_files" path="."/>
</paths>
This is work for every single file!
private void shareFile(File file) {
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
intentShareFile.setType(URLConnection.guessContentTypeFromName(file.getName()));
intentShareFile.putExtra(Intent.EXTRA_STREAM,
Uri.parse("file://"+file.getAbsolutePath()));
//if you need
//intentShareFile.putExtra(Intent.EXTRA_SUBJECT,"Sharing File Subject);
//intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File Description");
startActivity(Intent.createChooser(intentShareFile, "Share File"));
}
Thanks Tushar-Mate!
First you should define File Provider, see https://medium.com/#ali.dev/open-a-file-in-another-app-with-android-fileprovider-for-android-7-42c9abb198c1.
The code checks that a device contains applications which can receive the file, see How to check if an intent can be handled from some activity?.
fun sharePdf(file: File, context: Context) {
val uri = getUriFromFile(file, context)
if (uri != null) {
val intent = Intent().apply {
action = Intent.ACTION_SEND
type = "application/pdf" // For PDF files.
putExtra(Intent.EXTRA_STREAM, uri)
putExtra(Intent.EXTRA_SUBJECT, file.name)
putExtra(Intent.EXTRA_TEXT, file.name)
// Grant temporary read permission to the content URI.
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
// Validate that the device can open your File.
val activityInfo = intent.resolveActivityInfo(context.packageManager, intent.flags)
if (activityInfo?.exported == true) {
context.startActivity(Intent.createChooser(intent,
"Share PDF file")
}
}
}
fun getUriFromFile(file: File, context: Context): Uri? =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Uri.fromFile(file)
} else {
try {
FileProvider.getUriForFile(context, context.packageName + ".provider", file)
} catch (e: Exception) {
throw if (e.message?.contains("ProviderInfo.loadXmlMetaData") == true) {
Error("FileProvider is not set or doesn't have needed permissions")
} else {
e
}
}
}
Use ACTION_SEND_MULTIPLE for delivering multiple data to someone
intent.setAction(Intent.ACTION_SEND_MULTIPLE);
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, arrayUri);
intent.setType("text/plain");
startActivity(intent);
The arrayUri is the Array List of Uri of files to Send.
Here is an example to share or save a text file:
private void shareFile(String filePath) {
File f = new File(filePath);
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(filePath);
if (fileWithinMyDir.exists()) {
intentShareFile.setType("text/*");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + filePath));
intentShareFile.putExtra(Intent.EXTRA_SUBJECT, "MyApp File Share: " + f.getName());
intentShareFile.putExtra(Intent.EXTRA_TEXT, "MyApp File Share: " + f.getName());
this.startActivity(Intent.createChooser(intentShareFile, f.getName()));
}
}
File directory = new File(Environment.getExternalStorageDirectory() + File.separator + BuildConfig.APPLICATION_ID + File.separator + DIRECTORY_VIDEO);
String fileName = mediaModel.getContentPath().substring(mediaModel.getContentPath().lastIndexOf('/') + 1, mediaModel.getContentPath().length());
File fileWithinMyDir = new File(directory, fileName);
if (fileWithinMyDir.exists()) {
Uri fileUri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", fileWithinMyDir);
Intent intent = ShareCompat.IntentBuilder.from(this)
.setStream(fileUri) // uri from FileProvider
.setType("text/html")
.getIntent()
.setAction(Intent.ACTION_SEND) //Change if needed
.setDataAndType(fileUri, "video/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
val uriArrayList: ArrayList<Uri> = ArrayList()
GlobalScope.launch(Dispatchers.IO) {
runCatching {
itemsList!!.forEach {
uriArrayList.add(
FileProvider.getUriForFile(
mContext,
APPLICATION_ID + ".provider",
File(it.path)
)
)
}
}.onSuccess {
requireActivity().runOnUiThread {
if (uriArrayList.size > 0) {
val intent = Intent()
intent.action = Intent.ACTION_SEND_MULTIPLE
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriArrayList)
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
intent.type = "image/*|application/pdf/*"
startActivity(Intent.createChooser(intent, resources.getString(R.string.share)))
}
}
}
.onFailure {
Log.e("SHARING_FAILED", it)
}
}
First of all you have to write provider code in app manifest file for sharing on android 7.0 and above
<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>
here provider_paths are:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="/storage/emulated/0" path="."/>
<root-path name="root" path="." />
<files-path name="files" path="."/>
</paths>
Read this article about Sending Content to Other Apps
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));

Categories