Android Tutorial Sharing files from your app with File Provider

Erel

Administrator
Staff member
Licensed User
Example was updated based on FileProvider class: https://www.b4x.com/android/forum/threads/class-fileprovider-share-files.97865/
See the above link if you are interested in sharing files from your app.

This tutorial explains the steps required to implement a file picker that other applications can use to select files or resources.

upload_2016-8-29_13-51-54.png
upload_2016-8-29_13-52-7.png
upload_2016-8-29_13-52-19.png


Starting from Android 7 - Nougat,it is not possible to share a file directly (file:// uri). It will result with a FileUriExposedException. This is relevant even if you don't set the targetSdkVersion to 24.

The solution is to use FileProvider from Android support library.

The steps required are:
1. Add an intent filter that will tell the OS that your app allows choosing content:
B4X:
'manifest editor.
'FilePicker is the name of the activity.
AddActivityText(FilePicker,
<intent-filter>
  <action android:name="android.intent.action.PICK" />
  <category android:name="android.intent.category.DEFAULT" />
  <data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
  <action android:name="android.intent.action.GET_CONTENT" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.OPENABLE" />
  <data android:mimeType="image/*" />
</intent-filter>
)

3. When the relevant activity is resumed it needs to check the starting intent type. If it is an android.intent.action.GET_CONTENT intent then it should allow the user to select the resource.

4. The resource is copied to the "shared" folder and a Uri is created and returned to the calling app.

See the attached example.

Tips:

- Make sure to run the app in release mode as the process might be killed when it is in the background.
- This solution is compatible with Android 4+ devices.
 

Attachments

Last edited:

JohnC

Well-Known Member
Licensed User
Hey Erel,

First, thank you very much for whipping this up so quickly!

Second, is the secure file sharing aspect of this sample the only thing that requires Android 4+ devices? Or, can I still use the PICK and GET_CONTENT intents and be able to share a file using a direct URI on <4 devices? And if so, how would I modify the provider definition to alternatively provide this direct URI on <4 devices?
 

Erel

Administrator
Staff member
Licensed User

JohnC

Well-Known Member
Licensed User
Thank Erel.

I don't know how you get all the stuff you do done in the time you do it. You have to be using a cloning machine :)
 

JohnC

Well-Known Member
Licensed User
I wonder if using this new file sharing method will allow me to remove the STORAGE permission from my app because I am currently using a direct URI to send a file to the device's MMS using an intent...Hmmm
 

Erel

Administrator
Staff member
Licensed User
1. I've updated the example. It didn't add the storage permission for Android 4.3- devices.

I wonder if using this new file sharing method will allow me to remove the STORAGE permission from my app because I am currently using a direct URI to send a file to the device's MMS using an intent.
2. The example above doesn't need any permission on Android 4.4+ devices. You can read more about it in the runtime permissions tutorial.
 

JohnC

Well-Known Member
Licensed User
Darn, I was hoping this method used some sort of "safe" external_storage location so that I could share a file with another app without having to declare the STORAGE permission.

I know that I can use runtime permissions, but if my app absolutely needs to use the external_storage to operate, then what's the difference between asking/telling the user when they install my app vs. asking them when they run it. My understanding of runtime permissions is that it is good for apps that can still be useful in a "crippled" manner even if one or more permissions were denied by the user. But with my app, External_Storage is mandatory for it to do it's main function.
 

ivan.tellez

Active Member
Licensed User
Starting from Android 7 - Nougat,it is not possible to share a file directly (file:// uri).
Ok, I understand how to implement a file picker that other applications can use to select files or resources created with my app.

But, if I want to share a file created by my app, what is the best aproach?


Thanks
 

Erel

Administrator
Staff member
Licensed User

peggjones

Active Member
Licensed User
Try as I might I can't get this to compile. I get -

Error parsing manifest script:
Line = 35, Word = C
Command expected.

Where line 35 is CreateResource(xml, provider_paths,

Obviously missing something any ideas anyone?

Thanks
 

JohnC

Well-Known Member
Licensed User
Sometimes line number reported in the error is not the actual line of the error, but it could be within +/- 5 lines.
 

Robert Valentino

Well-Known Member
Licensed User
trying to understand the CreateResource keyword

B4X:
CreateResource(xml, provider_paths,
   <external-files-path name="name" path="shared" />
tried to search on it and found nothing.

What is xml, provider_paths and external-files-path better yet what are my Options?

I am trying to share a few string values, plus trying to learn how this could help me in the future

BobVal
 
Last edited:

Star-Dust

Expert
Licensed User
Thanks Erel, it works perfectly. I have updated almost all my Apps with the new features.

As usual with Samsung I have problems .. But I will open a special thread
 

JohnC

Well-Known Member
Licensed User
Starting from Android 7 - Nougat,it is not possible to share a file directly (file:// uri). It will result with a FileUriExposedException. This is relevant even if you don't set the targetSdkVersion to 24.
OK, I am concerned with the above statement because my app "shares"(sends) a file to the device's default MMS app via an Intent and it currently uses this method to do it:
B4X:
iIntent.PutExtra("android.intent.extra.STREAM", CreateUri("file://" & File.Combine(Starter.SafeDir, Filename)))

Sub CreateUri(uri As String) As Object
    Dim r As Reflector
    Return r.RunStaticMethod("android.net.Uri", "parse", Array As Object(uri), Array As String("java.lang.String"))
End Sub
And this works on 4.4 and 7.1 and 8.x devices.

So, why does this method, which uses "File://" work on 7.x and higher devices?

(p.s. The Starter.SafeDir = "rp.GetSafeDirDefaultExternal("shared")" )
 
Top