30

I'm working on an SDK that uses an internal ContentProvider, I would like to use this SDK in a few projects, and declare it in the library manifest, so I've tried this:

    <provider
        android:name=".core.MyContentProvider"
        android:authorities="${applicationId}"
        android:exported="false"/>

What happens is the ${applicationId} is replaced with the packageName of the library and not the top apk related applicationId...

Is there a way to make sure that the launching applicationId would be placed in the android:authorities value?

TacB0sS
  • 10,106
  • 12
  • 75
  • 118

3 Answers3

10

Was running into the same problem with several different variants and unique IDs, and ended up going with replacing a placeholder key when Gradle is building the app, kind of like so:

Gradle 3+

android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        output.processManifest.doLast {
            File manifestFile = file("$manifestOutputDirectory/AndroidManifest.xml")

            replaceInFile(manifestFile, 'P_AUTHORITY', variant.applicationId)
        }
    }
}

def replaceInFile(file, fromString, toString) {
    def updatedContent = file.getText('UTF-8')
            .replaceAll(fromString, toString)

    file.write(updatedContent, 'UTF-8')
}

Gradle < 3

android.applicationVariants.all { variant ->
    variant.outputs.each { output ->
        output.processManifest.doLast{
            replaceInManifest(output, 'P_AUTHORITY', variant.applicationId)
        }
    }   
}

def replaceInManifest(output, fromString, toString) {
    def manifestOutFile = output.processManifest.manifestOutputFile
    def updatedContent = manifestOutFile.getText('UTF-8').replaceAll(fromString, toString)
    manifestOutFile.write(updatedContent, 'UTF-8')
}

And then in the manifest:

<provider
    android:name=".core.MyContentProvider"
    android:authorities="P_AUTHORITY"
    android:exported="false"/>

That's come in handy quite a few times

Cruceo
  • 6,763
  • 2
  • 31
  • 52
  • 1
    This code must be placed in the top project Gradle file right... is there any workaround you can think of that would allow me to put this in the lib's Gradle file? – TacB0sS Jun 11 '15 at 20:59
  • Unfortunately I haven't yet figured out how to do that, and have just been letting the top-level build.gradle file handle it. I'm not really sure it'd be possible in the lib file, though, unless the library knew the variants / application ID for the projects they're being attached to before-hand, which is unlikely to be the case :( And yeah, it's definitely a hackier approach, but it's proven fairly useful in my use-cases. Hopefully it helps you out! – Cruceo Jun 11 '15 at 21:25
  • You know... I would have expected the manifest first to merge and then update the parameters... specifically the applicationId, it does not belong in any way to the library... it is only belongs to the top level project... – TacB0sS Jun 11 '15 at 21:29
  • I wish I knew enough about gradle to give more help, but sadly I don't yet understand it well enough to say why it would happen like that – Cruceo Jun 12 '15 at 14:18
  • I have successfully used this to tweak the manifest given to aapt for a `android:reference` attribute (which compains that `dollar_openBracket_foo_closeBracket` is not a valid reference). I used `android.libraryVariants` as it was for a lib project, and replaced `"dollar_openBracket_foo_closeBracket"` with some actual resource. But the most important change was to use `output.processManifest.aaptFriendlyManifestOutputFile` instead. Thanks for the solution basis, it could have taken me days otherwise! – ofavre Nov 27 '15 at 16:46
9

You can use ${applicationId} in under manifest file. Just make sure that in your gradle file of that library doesn't have "applicationId". If you declared it in your gradle file under "defaultConfig", please remove it.

//So your gradle file of library(SDK) module looks like..

defaultConfig {
minSdkVersion Version.minSdk
targetSdkVersion Version.targetSdk
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} 


//And your application gradle file looks like..

defaultConfig {
    applicationId "com.example.android"
    minSdkVersion Version.minSdk
    targetSdkVersion Version.targetSdk
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 }
Sagar Patel
  • 163
  • 1
  • 6
  • 1
    This is a better solution since it solves the problem without adding additional complexity. – ehartwell Oct 09 '19 at 20:33
  • Not sure why, but I had the same issue as OP (and no `applicationId` in lib gradle file), and when I tried this again it does indeed work – arekolek Jul 28 '21 at 15:15
  • I have this line in app/build.gradle above defaultConfig - 'namespace "com.somename"' . how does this affect package name? – Shehan DMG Mar 17 '23 at 06:21
2

Just make it a gradle flavor variable, it will load dynamic values for different flavors. I made this for FacebookContenProvider but the same principles apply

Manifest:

<provider android:authorities="@strings/authority"
        android:name="com.facebook.FacebookContentProvider"
        android:exported="true"/>

Gradle:

productFlavors {
    flavorName {
        ...
        resValue "string", "authority", "com.facebook.app.FacebookContentProvider'YOUAPPID'"
    }
}
Leandro
  • 180
  • 4