Android Question How to share multiple images on Android 7 without "FileUriExposedException"?

fredo

Well-Known Member
Licensed User
Longtime User
In order to share multiple images on Android 7 devices it is neccessary to use the FileProvider from the Android support library.

In Erel's example in the tutorial here the files should be copied to a shared folder to make them available for further use and then use the FileProvider.


This is where my problem starts, since I can't bring the puzzle together to make use of the FileProvider:

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="23"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddManifestText(<uses-permission
     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
     android:maxSdkVersion="18" />
)


AddApplicationText(
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="$PACKAGE$.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
)
CreateResource(xml, provider_paths,
    <external-files-path name="name" path="shared" />
)

B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    Private images As List = Array("B4A.png", "B4i.png", "B4J.png", "B4R.png")
    Private sharedFolder As String
End Sub

Sub Globals
End Sub

Sub Activity_Create(FirstTime As Boolean)
    sharedFolder = Starter.rp.GetSafeDirDefaultExternal("shared")
End Sub
Sub Activity_Resume
    Dim i As Intent
    i.Initialize("android.intent.action.SEND_MULTIPLE", "")
    i.SetType("image/jpeg")

    Dim Uris As List
    Uris.Initialize

    For Each image As String In images
        File.Copy(File.DirAssets, image, sharedFolder, image)

        ' ?? SetFileUri(res, image)

        '        Dim u As Uri
        '        u.Parse("file://" & File.Combine(Starter.strSharedFolder, image) )
        '        Uris.Add(u)
 
    Next

    Dim jo As JavaObject = i
    jo.RunMethod("putParcelableArrayListExtra", Array As Object("android.intent.extra.STREAM", Uris))
    StartActivity(i)

End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub


Sub SetFileUri(In As Intent, FileName As String)
    ' From here--> https://www.b4x.com/android/forum/threads/sharing-files-from-your-app-with-file-provider.70458/#content
    Dim context As JavaObject
    context.InitializeContext

    Dim FileProvider As JavaObject
    FileProvider.InitializeStatic("android.support.v4.content.FileProvider")

    Dim f As JavaObject
    f.InitializeNewInstance("java.io.File", Array(sharedFolder, FileName))

    Dim jo As JavaObject = In
    jo.RunMethod("setData", Array(FileProvider.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))))
    In.Flags = 0x00000001
End Sub

Edit:
As always. Due to Erels little push there is no problem since post #4 for API up to 23.

Edit:
For API 24 it is possible to share multiple images as described in post #9.
 
Last edited:

DonManfred

Expert
Licensed User
Longtime User
What is the question?
 
Upvote 0

fredo

Well-Known Member
Licensed User
Longtime User
The standard sharing code should work on Android 7.

You're right. Maybe I was mixing up what I read in your example and what I wanted to accomplish.

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="23"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddManifestText(<uses-permission
     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
     android:maxSdkVersion="18" />
)

B4X:
    #Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 2
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    ' Sample images in File.DirAssets
    Private images As List = Array("B4A.png", "B4i.png", "B4J.png", "B4R.png")
End Sub

Sub Globals
End Sub

Sub Activity_Create(FirstTime As Boolean)

    ' Make shure there is a share-folder
    Starter.strSharedFolder = Starter.rp.GetSafeDirDefaultExternal("shared")
 
    ' Delete old contents of this folder
    For Each f2del As String In File.ListFiles( Starter.strSharedFolder)
        File.Delete(Starter.strSharedFolder, f2del)
    Next
 
    ' Copy file to share to the share-folder
    For Each image As String In images
        File.Copy(File.DirAssets, image, Starter.strSharedFolder, image)
    Next
 
    ' Share those images
    ShareMultipleImages(images, Starter.strSharedFolder)
 
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub ShareMultipleImages(lstImageNames As List, strSrcFolder As String)
    Dim i As Intent
    i.Initialize("android.intent.action.SEND_MULTIPLE", "")
    i.SetType("image/jpeg")

    Dim Uris As List
    Uris.Initialize
 
    For Each image As String In images
        Dim u As Uri
        u.Parse("file://" & File.Combine(Starter.strSharedFolder, image )  )
        Uris.Add(u)
    Next
 
    Dim jo As JavaObject = i
    jo.RunMethod("putParcelableArrayListExtra", _
        Array As Object("android.intent.extra.STREAM", Uris))
    StartActivity(i) 
End Sub
 
Last edited:
Upvote 0

fredo

Well-Known Member
Licensed User
Longtime User
OK, while the code snippet from #4 worked up to API 23 there is a problem while targeting API 24 ("android.os.FileUriExposedException").

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="24"/>    ' <<<<<<<<<<<<<<<<<<<<<----------API 24
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddManifestText(<uses-permission
     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
     android:maxSdkVersion="18" />   ' <<<<<<<<<<<<<<<<<<<<<---------- tried 18 and 24 without success
)

'AddApplicationText(
'        <provider
'            android:name="android.support.v4.content.FileProvider"
'            android:authorities="$PACKAGE$.provider"
'            android:exported="false"
'            android:grantUriPermissions="true">
'            <meta-data
'                android:name="android.support.FILE_PROVIDER_PATHS"
'                android:resource="@xml/provider_paths"/>
'        </provider>
')
'CreateResource(xml, provider_paths,
'    <external-files-path name="name" path="shared" />
')

For some reasons my actual Android project needs to have targetSDK 24. So the problem is "How do I share images from API 24 up?"

B4X:
** Service (starter) Create **
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
main_sharemultipleimages (java line: 434)
android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/b4a.example/files/shared/B4A.png exposed beyond app through ClipData.Item.getUri()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
    at android.net.Uri.checkFileUriExposed(Uri.java:2346)
    at android.content.ClipData.prepareToLeaveProcess(ClipData.java:832)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8909)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
    at android.app.Activity.startActivityForResult(Activity.java:4224)
    at android.app.Activity.startActivityForResult(Activity.java:4183)
    at android.app.Activity.startActivity(Activity.java:4507)
    at android.app.Activity.startActivity(Activity.java:4475)
    at anywheresoftware.b4a.keywords.Common.StartActivity(Common.java:698)
    at b4a.example.main._sharemultipleimages(main.java:434)
    at b4a.example.main._activity_create(main.java:358)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
    at b4a.example.main.afterFirstLayout(main.java:102)
    at b4a.example.main.access$000(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:80)
    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:6077)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

This brings me to my hopefully better formulated question:

What do I have to do to share multiple images with targetAPI 24 without "FileUriExposedException"?
 

Attachments

  • ShareMultiImages3.zip
    30.3 KB · Views: 543
Last edited:
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Upvote 0

fredo

Well-Known Member
Licensed User
Longtime User
It should be a good start for sharing multiple images.
Thank you for the hint.
The attached Testproject now shares multiple images even with API 24.

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="24"/>
<supports-screens android:largeScreens="true"
    android:normalScreens="true"
    android:smallScreens="true"
    android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.

AddManifestText(<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
)

AddApplicationText(
  <provider
  android:name="android.support.v4.content.FileProvider"
  android:authorities="$PACKAGE$.provider"
  android:exported="false"
  android:grantUriPermissions="true">
  <meta-data
  android:name="android.support.FILE_PROVIDER_PATHS"
  android:resource="@xml/provider_paths"/>
  </provider>
)
CreateResource(xml, provider_paths,
   <external-files-path name="name" path="shared" />
)

B4X:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 5
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    ' Sample images in File.DirAssets
    Private images As List = Array("B4A.png", "B4i.png", "B4J.png", "B4R.png")
End Sub

Sub Globals
End Sub

Sub Activity_Create(FirstTime As Boolean)

    ' Make shure there is a share-folder
    Starter.strSharedFolder = Starter.rp.GetSafeDirDefaultExternal("shared")
 
    ' Delete old contents of this folder
    For Each f2del As String In File.ListFiles( Starter.strSharedFolder)
        File.Delete(Starter.strSharedFolder, f2del)
    Next
 
    ' Copy file to share to the share-folder
    For Each image As String In images
        File.Copy(File.DirAssets, image, Starter.strSharedFolder, image)
    Next
 
    ' Share those images
    ShareMultipleImages(images, Starter.strSharedFolder)
 
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub ShareMultipleImages(lstImageNames As List, strSrcFolder As String)
 
    Dim i As Intent
    i.Initialize("android.intent.action.SEND_MULTIPLE", "")
    i.SetType("image/jpeg")
 
    Dim Uris As List
    Uris.Initialize
 
    For Each image As String In images
        Dim u As Uri = CreateFileProviderUri(Starter.strSharedFolder, image)
        Uris.Add(u)
    Next
 
    Dim jo As JavaObject = i
    jo.RunMethod("putParcelableArrayListExtra", Array As Object("android.intent.extra.STREAM", Uris))
    i.Flags = 1
 
    StartActivity(i) 
 
End Sub

Sub CreateFileProviderUri (Dir As String, FileName As String) As Object
    Dim FileProvider As JavaObject
    Dim context As JavaObject
    context.InitializeContext
    FileProvider.InitializeStatic("android.support.v4.content.FileProvider")
    Dim f As JavaObject
    f.InitializeNewInstance("java.io.File", Array(Dir, FileName))
    Return FileProvider.RunMethod("getUriForFile", Array(context, Application.PackageName & ".provider", f))
End Sub

01-11-_2016_19-10-40.jpg
 

Attachments

  • ShareMultiImages4.zip
    30.3 KB · Views: 802
Last edited:
Upvote 0
Top