Android Intent.ACTION_SEND from Internal Storage with Content Provider - android

I am saving a file on internal storage. It is just a .txt file with some information about objects:
FileOutputStream outputStream;
String filename = "file.txt";
File cacheDir = context.getCacheDir();
File outFile = new File(cacheDir, filename);
outputStream = new FileOutputStream(outFile.getAbsolutePath());
outputStream.write(myString.getBytes());
outputStream.flush();
outputStream.close();
Then I am creating a "shareIntent" to share this file:
Uri notificationUri = Uri.parse("content://com.package.example/file.txt");
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, notificationUri);
shareIntent.setType("text/plain");
context.startActivity(Intent.createChooser(shareIntent, context.getResources().getText(R.string.chooser)));
The chosen app now needs access to the private file so I created a Content provider. I just changed the openFile method:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File privateFile = new File(getContext().getCacheDir(), uri.getPath());
return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
}
The manifest:
<provider
android:name=".ShareContentProvider"
android:authorities="com.package.example"
android:grantUriPermissions="true"
android:exported="true">
</provider>
When opening the Mail App to share the file it says, that it could not attach the file because it only has 0 Bytes. Sharing it via Bluetooth also failed. But I can read out the privateFile in the Content Provider, so it exists and it has content. What is the problem?

Thanks for pskink. FileProvider worked perfectly:
Gradle dependency:
compile 'com.android.support:support-v4:25.0.0'
Manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.package.example"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
file_paths.xml in XML folder:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="/" />
</paths>
Sharing Intent:
File file = new File(context.getCacheDir(), filename);
Uri contentUri = FileProvider.getUriForFile(context, "com.package.example", file);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.setType("text/plain");
context.startActivity(Intent.createChooser(shareIntent, context.getResources().getText(R.string.chooser)));

Related

ACTION_VIEW and FileProvider - how to open html file from sdcard in browser

I could not find a smart solution to my problem and that is why I decided to write to you good people. I have a question for your regarding FileProvider and ACTION_VIEW, namely, I save the html file this way:
File sdcard = getBaseContext().getExternalFilesDir(null);
File dir = new File(sdcard.getAbsolutePath() + “/MYDIR/“);
dir.mkdir();
File file = new File(dir, “MYFILE.html");
File recordFile = new File(sdcard.getAbsolutePath()+ "/MYDIR/" + "MYFILE.html");
FileOutputStream os = null;
try {
os = new FileOutputStream(file);
os.write(returnedText.getText().toString().getBytes());
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
its path: storage/emulated/0/Android/data/com.myapp/files/MYDIR/MYFILE.html
This is how I call my html file:
File sdcard = getBaseContext().getExternalFilesDir(null);
Intent intent = new Intent(Intent.ACTION_VIEW);
File f = new File(sdcard.getAbsolutePath() + "/MYDIR" + "/MYFILE.html");
intent.setDataAndType(Uri.fromFile(f), "text/html");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
Intent.FLAG_ACTIVITY_NO_HISTORY|
Intent.FLAG_ACTIVITY_NEW_TASK);
Intent.createChooser(intent, "File Browser");
startActivity(intent);
I want to call this MYFILE.html via Intent and open in HTML Viewer, I get this error:
ERR_ACCESS_DENIED
So I decided to try FileProvider, but I get a completely different path. What should I do, any specific idea??? Thanks in advance.
My FileProvider:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external_files"
path="." />
</paths>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.myapp.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>

File from FileProvider can't be viewed

I'm struggling with FileProvider. I want to open a video in another app, but no matter what I try every single app says that the video can't be loaded.
private void passVideo2(String videoname) {
File videoPath = new File(Environment.getExternalStorageDirectory(), "video_folder");
File videoInternalPath = new File(this.getFilesDir(), videoname);
File newFile = new File(videoPath, videoname);
Uri uri = FileProvider.getUriForFile(this, "com.example.provider", newFile);
Intent viewIntent = new Intent(Intent.ACTION_VIEW, uri);
viewIntent.setDataAndType(uri, getContentResolver().getType(uri));
viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
this.startActivity(viewIntent);
}
I've tried getting the video from the external and from the internal storage and I've tried different video formats and other file types like e.g. pdf and nothing seems to work
This is my provider_paths.xml
<paths>
<files-path name="files" path="/"/>
<external-path name="external" path="video_folder"/>
</paths>
and this is from the manifest
<provider
android:authorities="com.example.provider"
android:name="androidx.core.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>

Android Problem with sharing temporary file

I' m trying to create a temporary file and share it.
So I created this class:
public class GenerateFile {
public static File writeToFile(Context mcoContext, String sBody) {
String fileName = "LOG FILE_" + String.valueOf(System.currentTimeMillis()) +".txt";
File file = new File(mcoContext.getCacheDir(), fileName);
try{
FileWriter writer = new FileWriter(file);
writer.append(sBody);
writer.flush();
writer.close();
return file;
}catch (Exception e){
Toast.makeText(mcoContext, "File write failed: " + e.toString(), Toast.LENGTH_LONG).show();
}
return null;
}
}
to generate a file that after I will share here:
String logContent = "123";
File filePath = new File(file.getAbsolutePath(), "external_files");
filePath.mkdir();
Uri uri = FileProvider.getUriForFile(StatusActivity.this, getPackageName(), filePath);
Intent intent = ShareCompat.IntentBuilder.from(StatusActivity.this)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.setAction(Intent.ACTION_VIEW) //Change if needed
.setDataAndType(uri, "text/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
And in the manifest there are already this permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
<uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
and the provider declaration
<provider
android:name="android.support.v4.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>
The provider_paths class is defined in this way:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
But it generate the message, when I try to share it by mail or telegram "Unable to attach file" or "Unsupported attachment". Also it seems to me that the file is not created.
Other apps do not have access to your app's getCacheDir(). FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_WRITE_URI_PERMISSION are for content Uri values, not file Uri values. And, on Android 7.0+ devices, your code should crash with a FileUriExposedException.
Use FileProvider to make your content available to other apps, and use FileProvider.getUriForFile() to get the Uri to put in the Intent.
So I follow the suggestion of #CommonsWare, and I edited my code. This is the final result:
public class GenerateFile {
public static Uri getFileURI(Context context, String nameFile, String content, String fileExtension) {
DateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd");
Date date = new Date();
String fileName = dateFormat.format(date)+nameFile+fileExtension;
File file = new File(context.getCacheDir(), fileName);
try{
FileWriter writer = new FileWriter(file);
writer.append(content);
writer.flush();
writer.close();
//Toast.makeText(context, "Writing to the file completed successfully", Toast.LENGTH_LONG).show();
}catch (Exception e){
Toast.makeText(context, "File writing failed: " + e.toString(), Toast.LENGTH_LONG).show();
}
File filePath = new File(context.getCacheDir(), "");
File newFile = new File(filePath, fileName);
return FileProvider.getUriForFile(context, "MYPACKAGE.fileprovider", newFile);
}
}
and in another class:
private void sendFile(String nameFile, String logContent, String fileExtension) {
Uri contentUri = GenerateFile.getFileURI(getApplicationContext(), nameFile, logContent, fileExtension);
Intent intent = ShareCompat.IntentBuilder.from(StatusActivity.this)
.setStream(contentUri) // uri from FileProvider
.setType("text/plain")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "send"));
}
so to send the file. I also deleted the permission (previously mentioned) in the manifest, because I didn't need it anymore.
And I also edited my provider and provider_path file like that:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="MYPACKAGE.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<cache-path
name="my_files"
path=""
/>
Now it works! Thank you very much guys for the help!
Did you specifically ask the user for those permissions? It's not enough to just put the permissions in the manifest for target sdks below 28. Also, in Android Q, you will need to work around external storage permissions altogether as this is disallowed.

Unable to launch a PDF viewer in Nougat (7.1)

I am creating a app in which i am generating pdf and after this app showing PDF automatically. it is working wee in 6.1 android but not showing in android 7.1. my code is
public void showPDF() {
File pdfFile = new File(Environment.getExternalStorageDirectory() , "A_DailyRegiser.pdf");
try {
if (pdfFile.exists()) {
Uri path = Uri.fromFile(pdfFile);
Intent objIntent = new Intent(Intent.ACTION_VIEW);
objIntent.setDataAndType(path, "application/pdf");
// objIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
objIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
objIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(objIntent);
} else {
Toast.makeText(dailycollection.this, "File NotFound",Toast.LENGTH_SHORT).show();
}
} catch (ActivityNotFoundException e) {
Toast.makeText(dailycollection.this,
"No Viewer Application Found", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
That's because of the new changes in Noughat API's Read more here
you are suggested to use the content:// scheme instead of file:// for this we can use the file provider API.
here is sample code:
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
...
</application>
define the scope of your access
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
Generating the Content URI for a File
File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
Granting Temporary Permissions to a URI
Intent.setFlags() with either FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION or both.
read more here

Unable to save file from FileProvider

I am capturing images from android application and saving images in external storage using Fileprovider. But it is giving me an exception as follow
java.lang.IllegalArgumentException:
Failed to find configured root that contains /data/data/com.rocketstove/files/SAMS/JMC-R-1256655/application_form_first.jpg
I have configured provider in androidManifest like this
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
I have also add following line in file_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
In java code
if (Build.VERSION.SDK_INT >= 23) {
File imagePath = new File(getActivity().getFilesDir(), "SAMS");
if (!imagePath.exists()) {
imagePath.mkdir();
}
imagePath = new File(getActivity().getFilesDir() + "/SAMS", rocketId);
if (!imagePath.exists()) {
imagePath.mkdir();
}
File imageFile = new File(imagePath.getPath(), filename);
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = getActivity().getApplicationContext().getPackageName() + ".fileprovider";
Uri uri = FileProvider.getUriForFile(getActivity(), authority, imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivity(intent);
}
Edit
file_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="external_files" path="Android/data/com.rocketstove.files/"/>
</paths>
Java code
if (Build.VERSION.SDK_INT >= 23) {
File file
=getActivity().getExternalFilesDir(null);
File imagePath = new File(file , rocketId);
if (!imagePath.exists()) {
imagePath.mkdir();
}
File imageFile = new File(imagePath.getPath(), filename);
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = getActivity().getApplicationContext().getPackageName() + ".fileprovider";
Uri uri = FileProvider.getUriForFile(getActivity(), authority, imageFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivity(intent);
}
Error
ava.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.rocketstove/files/JMC-R-4555555/application_form_first.jpg
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:719)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:404)
I have also add following line in file_paths
You have external-path. The documentation states that this:
Represents files in the root of the external storage area. The root path of this subdirectory is the same as the value returned by Environment.getExternalStorageDirectory().
That is not where your File points to.
Change external-path to files-path, since your Java code uses getFilesDir().

Categories

Resources