Frage

As part of an Adobe Flex application, I am writing an android native extension to allow me to send emails with file attachments using files generated from flex code. However I don't want the file to be world readable as it might contain sensitive data. Therefore I want to send the file from within my app's internal storage area / cache. I am using Intents to communicate with other apps (such as Gmail) to send the email.

After doing some research, I discovered that the functionality of FileProviders should do exactly what I want. However the static method FileProvider.getUriForFile() is silently failing / crashing the native extension. The native extension stops and returns null with no errors or output from LogCat.

If I manually create the Uri by parsing a string, Gmail complains that it can't attach the file to the email, and the email is sent with no attachment.

Code in FREObject call():

//... (Deal with params from flex app) ...

//Setup Intent, and attach email data send from flex application:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("message/rfc822");
intent.putExtra(Intent.EXTRA_EMAIL, toArray);
intent.putExtra(Intent.EXTRA_CC, ccArray);
intent.putExtra(Intent.EXTRA_BCC, bccArray);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, message);

Log.d("emaildebug", filePath);
//Check we can read our email attachment from flex app
//filePath is a temporary cache location.
File appFile = new File(filePath);
if (appFile.exists() && appFile.canRead()) {
    Log.d("emaildebug", "file successfully read");
} else {
    return null;
}

//Get a handle on the android Context instead of FREContext. 
Context androidContext = (Context) context.getActivity();

//Get the location of the root files directory.
File attachPath = androidContext.getFilesDir();
File attachFile = new File(attachPath, "attachment.pdf");

//Copy file to root of files directory, so that our FileProvider can access it.
try {
    copyFileUsingChannel(appFile, attachFile);
} catch (Exception e) {
    Log.d("emaildebug", e.getMessage());
}

Log.d("emaildebug", "attachFile exists: " + attachFile.exists() );
Log.d("emaildebug", "attachFile path: " + attachFile.getAbsolutePath());

//This line will silently crash the native extension, instantly returning null, even in try catch.
//Uri contentUri = FileProvider.getUriForFile(androidContext, "com.example.androidextensiontest.provider", attachFile);

//Therefore manually create the Uri from a string.
Uri contentUri = Uri.parse("content://com.example.androidextensiontest.provider/files/attachment.pdf");

Log.d("emaildebug", "uri created");
Log.d("emaildebug", contentUri.toString());

//Grant permisions for all apps that can handle given intent
//Courtesy of: http://stackoverflow.com/questions/18249007/how-to-use-support-fileprovider-for-sharing-content-to-other-apps
List<ResolveInfo> resInfoList = androidContext.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    Log.d("emaildebug", "package: " + packageName);
    androidContext.grantUriPermission(packageName, contentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, contentUri);

context.getActivity().startActivity(Intent.createChooser(intent, "Send mail using..."));

In Manifest File:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.androidextensiontest.provider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/my_paths" />
</provider>

In my_paths.xml:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="." />
</paths>

LogCat output:

11-01 10:58:09.971: D/emaildebug(17013): /data/data/air.com.example.MyAppName.debug/cache/FlashTmp.V17013/attachment.pdf
11-01 10:58:09.971: D/emaildebug(17013): file successfully read
11-01 10:58:09.991: D/emaildebug(17013): attachFile exists: true
11-01 10:58:09.991: D/emaildebug(17013): attachFile path: /data/data/air.com.example.MyAppName.debug/files/attachment.pdf
11-01 10:58:09.991: D/emaildebug(17013): uri created
11-01 10:58:09.991: D/emaildebug(17013): content://com.example.androidextensiontest.provider/files/attachment.pdf
11-01 10:58:09.991: D/emaildebug(17013): package: com.android.email
11-01 10:58:09.991: D/emaildebug(17013): package: com.google.android.gm

If I attempt to send the file from external storage using a file:// Uri, the file attachment works perfectly. However as previously mentioned the file attachment potentially contains sensitive data, therefore I wish to avoid using external storage.

What I believe I'm seeing is that FileProviders do not work within native extensions. I tried also using a ContentProvider, however neither the Constructor nor onCreate methods were called.

ContentProvider Manifest:

<provider 
    android:name="com.example.androidextensiontest.provider.MyContentProvider"
    android:authorities="com.example.androidextensiontest.provider">
</provider>

MyContentProvider:

public class MyContentProvider extends ContentProvider implements PipeDataWriter<InputStream> {

public MyContentProvider() {
    // Never Output
    Log.d("emaildebug", "Constructor");
}

@Override
public boolean onCreate() {
    // Never Output
    Log.d("emaildebug", "OnCreate");
    return false;
}

//.. Rest of class
}

Am I doing something wrong or are ContentProviders / FileProviders just not supported within flex native extensions? Alternatively, are there any other solutions for attaching a file located in internal storage to an email. My searching on SO and google has not encountered anyone else having similar experiences, especially regarding FileProvider.getUriForFile(). Any help / comments appreciated.

Sorry for the wall of text, I wanted to record everything I've tried so far, and what works and what doesn't.

tldr; Do ContentProviders / File Providers work in flex native extensions? Are there any other methods of sending internal files as email attachments?

War es hilfreich?

Lösung

So I've finally had time to come back and look at this issue. I've learned a bit more about the flex extension build process and android development in general.

I now have a fully working fileprovider in an adobe flex extension.

The first part of the solution is that the manifest in the .ane is ignored for flex extensions. Instead any activities or providers must be provided in appname-app.xml in flashbuilder under the manifest extensions:

<android>
    <colorDepth>16bit</colorDepth>
    <manifestAdditions><![CDATA[
        <manifest android:installLocation="auto">
            <application android:enabled="true"
                android:launchMode="singleInstance">
                <provider
                    android:name="android.support.v4.content.FileProvider"
                    android:authorities="com.example.androidextensiontest.files"
                    android:exported="false"
                    android:grantUriPermissions="true">
                    <meta-data
                        android:name="android.support.FILE_PROVIDER_PATHS"
                        android:resource="@xml/file_paths" />
                </provider>
            </application>
        </manifest>
    ]]></manifestAdditions>
</android>

This is will cause flashbuilder to complain that it can't find @xml/file_paths resource.

Therefore you need to copy the entire res/ folder from the android extension into the ane build/android folder where you build the ane.

So the build folder structure looks like this:

-BuildFolder
    -android
        -libs
        -res
            -otherfolders
            -xml
                file_paths.xml
        androidLib.jar
        library.swf
    -default
    -ios 

Build ane as normal. After this my fileprovider started working meaning you can use this line to generate a fileprovider uri:

Uri contentUri = FileProvider.getUriForFile(androidContext, "com.example.androidextensiontest.files", attachFile);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top