7

In a library project I'm using a FileProvider with my libraries applicationId as its authority. Now when somebody uses this library e.g. in two flavors of his app, the second one will not install because of conflicting FileProviders.

Goal

Being able to define a unique authority for my libraries FileProvider for each variant of an application.

First Approach

Im appending a random int to the authority, which works just fine.

build.gradle

static def generateContentProviderAuthority() {
    Random random = new Random()
    return new StringBuilder()
            .append("application.id.of.my.library")
            .append(".fileprovider")
            .append(String.valueOf(random.nextInt(100000)))
            .toString()
}


defaultConfig {
    ...
    manifestPlaceholders= [contentProviderAuthority: generateContentProviderAuthority()]
}

Manifest

<provider
    android:name=".helper.FileProvider"
    android:authorities="${contentProviderAuthority}"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths"/>
</provider>

But now the manifest changes every build, which messes with instant run and stuff...

Solution

Is it possible to get the exact applicationId of the application which uses the library in my libraries build.gradle?

Jonas
  • 1,432
  • 3
  • 20
  • 27
  • 7
    I put the `` element in the app's manifest, or at least have the app's manifest supply the `android:authorities` value (via manifest merger). That way, particularly if you mangle `${applicationId}` into the `android:authorities` value, the authority string can be unique, even if multiple apps use the library. The library then derives the authority string off of `getPackageName()`. – CommonsWare May 19 '17 at 15:44
  • @CommonsWare I tried to do just that, but in order to access a file (`FileProvider.getUriForFile`) I need access to the authority as `String`. Do you know how I can get it? Since I am using the `FileProvider` in a library module that may be used in any app I have no access to `BuildConfig.APPLICATION_ID`. Thanks! – Markus Penguin Jul 16 '20 at 10:01
  • @MarkusPenguin: The safest thing is to have a method in your library that the hosting app calls to give you the authority string. You can try `getPackageName()` as I mentioned, but AFAIK that assumes that the app is not changing the application ID based on build variant. – CommonsWare Jul 16 '20 at 10:45

2 Answers2

7

As CommonsWare mentioned in the comment, it is enough to use dynamic ${applicationId} in the authorities section.

<provider
    android:name=".ScreenshotFileProvider"
    android:authorities="${applicationId}.library.name.screenshots"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_provider_paths"/>
</provider>

More thorough explanation at CommonsWare's blog: https://commonsware.com/blog/2017/06/27/fileprovider-libraries.html

Antimonit
  • 2,846
  • 23
  • 34
  • 4
    When using `FileProvider.getUriForFile` I'll still need to provide the authority. Do you know how I can get it? `BuildConfig.APPLICATION_ID` is not available, because I am using the provider in a module that may be included in any app project. – Markus Penguin Jul 16 '20 at 09:59
0

In library manifest put code like below:

<provider
android:name="YOUR LIBRARY FILE PROVIDER CLASS"
android:authorities="${applicationId}.library.file.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/provider_paths"
    tools:node="merge" />

Then have a class like below in your sdk and then initialise it with context from consumer application or from your sdk content provider.

object SDK {

  lateinit var fileProviderAuthority: String

  fun init(context: Context) {
   fileProviderAuthority = "${context.packageName}.library.file.provider"
  }

}

When you need the fileAuthority string for file uri operations consume it like below:

val uri = FileProvider.getUriForFile(this, SDK.fileProviderAuthority,File(FILE_PATH))

To read more click here

Rajesh.k
  • 2,327
  • 1
  • 16
  • 19