0

I've asked a similar question to this before, however, I have done some more research and I am still struggling to figure out what my problem is. Essentially, what I am trying to do is create a form of receipt in a PDF format after a customer has completed their purchase. This newly created PDF needs to then be sent to a separate emailing client (such as Gmail) through an Intent. Compared to my previous question I have now implemented a FileProvider, however I keep getting two different errors. The first one is:

java.lang.SecurityException: Permission Denial: reading androidx.core.content.FileProvider uri content...requires the provider be exported, or grantUriPermission()

This causes the file to not be attached to the email at all. And the second error I can sometimes get is, that it will place the actual URI path into the Recipients section of the email, which you can see below (also causing the file to not be attached)

enter image description here

I have tried quite a few different solutions upon doing my research, such using setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) or addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION), which can be seen here

I've also seen that some posts that said to use android:requestLegacyExternalStorage="true" in the manifest, however, this will then limit my minimum APK level quite a lot.

I even tried what this post suggests, whereby you manually go through and give permissions to all the packages, which still didn't solve my problem.

I've also tried using the grantUriPermission() method, which still didn't work either.

Below, you will see that I have placed some code:

Manifext.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.fabricanddecor">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:largeHeap="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".LoginActivity"
            android:configChanges="orientation|screenSize"
            android:theme="@style/AppTheme.NoActionBar"
            android:windowSoftInputMode="adjustPan">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize"
            android:theme="@style/AppTheme.NoActionBar"
            android:windowSoftInputMode="adjustPan">
        </activity>
        <provider
            android:authorities="com.example.fabricanddecor"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

    </application>

</manifest>

provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_files"
        path="."/>
</paths>

Method that creates the email and fires Intent (EDIT)

try {
            String directory = Environment.getExternalStorageDirectory().getPath();
            File file = new File(directory);
            if (file.mkdirs()) {
                String filePath = directory + "/sale.pdf";
                File file2 = new File(filePath);
                Uri contentUri = FileProvider.getUriForFile(getContext(), "com.example.fabricanddecor", file2);
                Intent intent = new Intent(Intent.ACTION_SEND);

                getContext().grantUriPermission("com.example.fabricanddecor", contentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

                intent.putExtra(Intent.EXTRA_EMAIL, recipient);
                intent.putExtra(Intent.EXTRA_SUBJECT, subject);
                intent.putExtra(Intent.EXTRA_STREAM, contentUri);
                intent.setDataAndType(contentUri, "application/pdf");
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                startActivity(Intent.createChooser(intent, "Choose an Email Service"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

Any help or suggestion will be highly appreciated, and thank you for your time. If any further code is required, I will be happy to oblige.

James Foster
  • 71
  • 1
  • 7
  • In your manifest, you define the provider with: android:exported="false" have you tried setting it to true? seeing as that's what the error you write requires – Dan Baruch Oct 22 '20 at 07:22
  • `catch (IOException ioe) { Log.e("File Error", "error" + ioe.toString()); }` After such an exception you do as it was not there and you continue with the intent. Instead you should stop then of course. Display a toast so the user knows. – blackapps Oct 22 '20 at 07:39
  • You are creating a pdf file and then sharing it. Those are two things. If you can create a pdf file then you do not need to post that code. Please post only code that is problematic. I think only sharing is problematic. – blackapps Oct 22 '20 at 07:41
  • `file.mkdirs();` only call mkdirs if the directory does not exist yet. And check the return value. If it returns false then stop/return and display a toast. – blackapps Oct 22 '20 at 07:44
  • `String fileDirectory = directory+"sale.pdf` ? Directory? Better name it: `String filePath = directory+"sale.pdf";` – blackapps Oct 22 '20 at 07:47
  • But... Why did you add that trailing slash for `directory'? Remove it so you can use `String filePath = directory +"/sale.pdf";` – blackapps Oct 22 '20 at 07:49
  • @blackapps Sorry about that, I meant to include it just for context sake, I will edit it to be more precise. I also didn't notice the the fact that it wasn't in my try clause, so thank you for picking that up for me. I renamed all the one variable, removed `mkdirs()` and also removed the trailing slash – James Foster Oct 22 '20 at 09:40
  • You should not remove mkdirs when you are trying to create your file. – blackapps Oct 22 '20 at 09:47
  • `document.writeTo(new FileOutputStream(filePath)); document.close();` Remove those lines as you now post code for sharing an existing file. – blackapps Oct 22 '20 at 09:50
  • @blackapps Would the code look something like what I added ? – James Foster Oct 22 '20 at 13:54
  • @DanBaruch Setting it to `True` crashes the app on startup, I've read that users battle with this problem when running the app on Android Q – James Foster Oct 22 '20 at 13:55
  • No. As you blindly call mkdirs. Look at my comment about mkdirs how to do it instead. But... If you wanna share a file then... You wil not create a directory there as of course it exists already otherwise your file would not exist either. – blackapps Oct 22 '20 at 14:40

0 Answers0