Jump to content
Francisco

Android. FileUriExposedException: file:///

Recommended Posts

When targeting newer Android SDK for uploading an app to Google's store I obtain an error when sending a file as an attachment:

 

android.os.FileUriExposedException: file:///storage/emulated/0/Documents/filename.jpg exposed beyond app through ClipData.Item.getUri()

 

The code was working fine in Tokyo.

 

procedure CreateEmail(const Recipient, Subject, Content, Attachment: string);


var

  JRecipient: TJavaObjectArray<JString>;
  Intent: JIntent;
  Uri: Jnet_Uri;
  AttachmentFile: JFile;


begin


 

  JRecipient := TJavaObjectArray<JString>.Create(1);
  JRecipient.Items[0] := StringToJString(Recipient);
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_EMAIL, JRecipient);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(Subject));
  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(Content));

  if Attachment <> '' then
  begin
    AttachmentFile := TJFile.JavaClass.init(StringToJString(Attachment));
    Uri := TJnet_Uri.JavaClass.fromFile(AttachmentFile);
    Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Uri as ILocalObject).GetObjectID));
  end;

   Intent.setType(StringToJString('vnd.android.cursor.dir/email'));


TandroidHelper.Activity.startActivity(Intent);
   
end;

 

I have tried different codes I found over there. No one works.

 

I have activated "Secure File Sharing" in Project Options.

 

I have tried a code using DW.Androidapi.JNI.FileProvider.pas

 

But I cannot get one working.

 

Thanks in advance.

Edited by Francisco

Share this post


Link to post

You are simply not allowed to use `file://` URLs to share files via Intents anymore.  Which means your call to

Uri := TJnet_Uri.JavaClass.fromFile(AttachmentFile);

Will still produce a valid 'file://' URL, you just can't use it in your Intent anymore.

 

You need to add a FileProvider to your Android app and manifest, and then you can use 'content://' URLs serviced by that provider for any files you want to share access to.

 

See Sharing files and Sharing files through Intents: are you ready for Nougat? for more details.

Edited by Remy Lebeau

Share this post


Link to post

Thanks Remy.

 

This seems tricky.

 

The first thing I have done is including in the manifest file the following code:

 

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.FJAG.StatSuite.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
 </provider>

 

 

 

Then I create a file with name filepaths.xml with the following content:

 

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

 

In RAD Studio, deployment, I add this file with remote path: res\xml\

 

 

My procedure to send the email with an attachmet is:

 

procedure CreateEmail(const Recipient, Subject, Content, Attachment: string);


var

  JRecipient: TJavaObjectArray<JString>;
  Intent: JIntent;
  Uri, data: Jnet_Uri;
  AttachmentFile: JFile;


begin


 JRecipient := TJavaObjectArray<JString>.Create(1);
  JRecipient.Items[0] := StringToJString(Recipient);
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_EMAIL, JRecipient);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(Subject));
  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(Content));

  if TJBuild_VERSION.JavaClass.SDK_INT >= TJBuild_VERSION_CODES.JavaClass.N then
  begin
    AttachmentFile := TJFile.JavaClass.init(StringToJString(Attachment));
    Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
    Data := TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,
      StringToJString('com.FJAG.StatSuite.fileprovider'), AttachmentFile);
  end
  else
    Data := TJnet_Uri.JavaClass.parse(StringToJString('file://' + Attachment));


    Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Data as ILocalObject).GetObjectID));

    TAndroidHelper.Activity.startActivity(Intent);


end;

 

The error when calling this procedure is:

 

android.content.ActivityNotFoundException: No activity found to handle Intent {act=android.intent.action.SEND flg=0x1 launchParam=MultiScreenLaunchParams {mDisplayld=0

mFlags=0} clip={null T:A StatSuite file is attached} (has extras)}

 

 

 

"A StatSuite file is attached" is the subject of the email

 

 

Please, a couple of questions:

 

1.- Must I activate "Secure File Sharing" in the Entitlement List? (it is activated now)

 

2.- What am I doing wrong?

 

Thanks in advance

Edited by Francisco

Share this post


Link to post
6 hours ago, Francisco said:

Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);

That should be using addFlags() instead of setFlags(). By using setFlags(), you are overwriting your FLAG_ACTIVITY_NEW_TASK flag.

6 hours ago, Francisco said:

Data := TJnet_Uri.JavaClass.parse(StringToJString('file://' + Attachment));

You can still use TJnet_Uri.JavaClass.fromFile() in earlier Android versions.  You didn't need to change that part of the code to use TJnet_Uri.JavaClass.parse() instead.

Share this post


Link to post

Remy, I modified that line to:

 

Intent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);

 

And I am still obtaining the same error:

 

android.content.ActivityNotFoundException: No activity found to handle Intent {act=android.intent.action.SEND flg=0x1 launchParam=MultiScreenLaunchParams {mDisplayld=0

mFlags=0} clip={null T:A StatSuite file is attached} (has extras)}

 

 

 

 

Share this post


Link to post
17 minutes ago, Francisco said:

android.content.ActivityNotFoundException: No activity found to handle Intent {act=android.intent.action.SEND

You need to call Intent.setType with an appropriate type for email, e.g.

 

plain/text

message/rfc822
application/octet-stream

 

If that still does not work, then there's probably no email client installed.

Share this post


Link to post

It works now!!!

 

Tested in Android 7 and 9

 

I added:

Intent.setType(StringToJString('vnd.android.cursor.dir/email'));

 

Therefore, I would like to share a procedure that permits to send an email with attachment (any type of file) in Android. Do not forget to modify the AndroidManifest file and to create the xml file, as commented before.

 

 

procedure CreateEmail(const Recipient, Subject, Content, Attachment: string);


var

  JRecipient: TJavaObjectArray<JString>;
  Intent: JIntent;
  Uri, data: Jnet_Uri;
  AttachmentFile: JFile;

begin


 JRecipient := TJavaObjectArray<JString>.Create(1);
  JRecipient.Items[0] := StringToJString(Recipient);
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_EMAIL, JRecipient);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(Subject));
  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(Content));

  if TJBuild_VERSION.JavaClass.SDK_INT >= TJBuild_VERSION_CODES.JavaClass.N then
  begin
    AttachmentFile := TJFile.JavaClass.init(StringToJString(Attachment));
    Intent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
    Data := TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,
      StringToJString('com.FJAG.StatSuite.fileprovider'), AttachmentFile);
  end
  else
    Data := TJnet_Uri.JavaClass.parse(StringToJString('file://' + Attachment));


    Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Data as ILocalObject).GetObjectID));

    Intent.setType(StringToJString('vnd.android.cursor.dir/email'));

    TAndroidHelper.Activity.startActivity(Intent);


end;

 

 

Thanks a lot to Remy and Dave!!

 

 

Share this post


Link to post

Hey, i'm trying to follow your example.=)

But i get a problem. 

It does not work.... XD

i get a popup:

java.lang.nullpointerException:attempt to invoke virtual method

'android.content.res.xmlResourceParser

android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager,Java.lang.String)' on a null object reference.

Do you have any idea, what the problem could be?

I already tried it with files-path vs. external-path in the xml, with or without <paths xmlns:android="http://schemas.android.com/apk/res/android">, activated "save exchange" and wrote and deleted the provider in different parts of the manifest.=/

Share this post


Link to post
2 hours ago, MichaMD said:

i get a popup:

java.lang.nullpointerException:attempt to invoke virtual method

'android.content.res.xmlResourceParser

android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager,Java.lang.String)' on a null object reference.

This is usually because the Secure File Sharing checkbox in the Entitlements List in the Project Options is unchecked, and it needs to be checked

Share this post


Link to post
5 hours ago, Dave Nottage said:

This is usually because the Secure File Sharing checkbox in the Entitlements List in the Project Options is unchecked, and it needs to be checked

That was what i meant with "activated "save exchange" ", got lost in translation.
Something i only found here.^^

My Code example:

unit Main;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, System.IOUtils, System.Messaging, System.StrUtils,
  FMX.Edit, FMX.Layouts, Androidapi.JNI.Net, Androidapi.JNI.App, Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os, Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers,
  Androidapi.JNIBridge, Androidapi.JNI.Media, Androidapi.JNI.Support;

type
  TForm1 = class(TForm)
    GridPanelLayout1: TGridPanelLayout;
    Button1: TButton;
    EmailAddy: TEdit;
    Betreff: TEdit;
    Text: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure CreateCSVForAttachment(Path:string);
    Procedure SendMail(const Recipient, Subject, MailBody, Attachment: string);
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
var
  FileToWrite: string;
  TestRecipient: string;
  TestSubject: string;
  TestMailBody: string;
begin
  FileToWrite:='Example.csv';
  TestRecipient:='Dummy.Dummy@Dummy.de';  
  TestSubject:='Emailtest';
  TestMailBody:='blabla';
  CreateCSVForAttachment(System.IOUtils.TPath.GetDocumentsPath + System.SysUtils.PathDelim + FileToWrite); //crate file to try sending
  SendMail(TestRecipient,TestSubject,TestMailBody,System.IOUtils.TPath.GetDocumentsPath + System.SysUtils.PathDelim + FileToWrite);
end;

Procedure TForm1.CreateCSVForAttachment(Path:string);
var
  strList: TStringList;
  a:string;
begin
  StrList := TStringList.Create;
  a:='Lorem ipsum dolor sit amet, consetetur sadipscing elitr,';
  StrList.Add(a);
  a:='Lorem ipsum dolor sit amet: consetetur sadipscing elitr:';
  StrList.Add(a);
  a:='Lorem ipsum dolor sit amet; consetetur sadipscing elitr;';
  StrList.Add(a);
  try
    begin
      StrList.SaveToFile(Path);
      showmessage(Path);
    end;
  finally
    StrList.Free;
  end;
end;

Procedure TForm1.SendMail(const Recipient, Subject, MailBody, Attachment: string);
var
Intent: JIntent;
listRecipients: TJavaObjectArray<JString>;
Data:Jnet_Uri;
AttachmentFile: JFile;
begin
  listRecipients:=TJavaObjectArray<JString>.Create(1);
  listRecipients.Items[0]:= StringToJString(Recipient);

  Intent:= TJIntent.create;
  Intent.setAction(TJIntent.javaClass.ACTION_SEND);
  Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);                   //Found many Codeexamples without this part, necessary?
  Intent.putExtra(TJIntent.javaClass.EXTRA_EMAIL,listRecipients);
  //Intent.putExtra(TJIntent.javaClass.EXTRA_CC,listRecipients);
  //Intent.putExtra(TJIntent.javaClass.EXTRA_Bcc,listRecipients);
  Intent.putExtra(TJIntent.javaClass.EXTRA_SUBJECT,StringToJString(Subject));
  Intent.putExtra(TJIntent.javaClass.EXTRA_TEXT,STringToJString(MailBody));

  if Attachment<>'' then
    begin
      if TJBuild_Version.JavaClass.SDK_INT>=TJBuild_VERSION_CODES.JavaClass.N then
        begin
          AttachmentFile := TJFile.JavaClass.init(StringToJString(Attachment)); //add  'internal_files/'+   ?
          Intent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
          Data:=TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,
          StringToJString('com.embarcadero.EMail.fileprovider'),AttachmentFile);// Found many Codeexamples with .provider in contrast . fileprovider, 							//com.embarcadero.projectname.fileprovider ?				
        end               
      else
        begin
          Data:=TJnet_Uri.JavaClass.parse(STringToJString('file://'+Attachment));
        end;
      Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Data as ILocalObject).GetObjectID));
                                                                                //found often TJnet_Uri.JavaClass.fromFile(AttachmentFile); ?
    end;

  Intent.setType(StringToJString('vnd.android.cursor.dir/mail'));               {tryed "text/plain" or "*/*" or message/rfc822 or vnd.android.cursor.dir/mail or vnd.android.cursor.dir/*}

  TAndroidHelper.Activity.startActivity(Intent);
end;

end.

Changed AndroidManifest.template.xml:

from:

 <application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%"
        android:resizeableActivity="false">

        <%provider%>
        <%application-meta-data%>
        <%uses-libraries%>

to:

<application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%"
        android:hardwareAccelerated="%hardwareAccelerated%"
        android:resizeableActivity="false">

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.embarcadero.Email.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
 	    </provider>
        <%application-meta-data%>
        <%uses-libraries%>
        <%services%>

And added Android/Debug/Email/res +xml/filepaths.xml

with:

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

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×