39

I am using Gradle with Product flavors where I set a different package name for each one.

productFlavors {

    appone {
        packageName "com.dg.app1"
    }

    apptwo {
        packageName "com.dg.app2"
    }

    appthree {
        packageName "com.dg.app3"
    }

    appfour {
        packageName "com.dg.app4"
    }

}

I need to be able to replace the package name inside the manifest for each corresponding app.

My manifest has this:

<receiver android:name="com.parse.GcmBroadcastReceiver"
          android:permission="com.google.android.c2dm.permission.SEND">
  <intent-filter>
    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

    <category android:name="com.dg.example" />
  </intent-filter>
</receiver>

So I need to replace com.dg.example for each app flavor's package name. What is the best way to do this?

Floern
  • 33,559
  • 24
  • 104
  • 119
DArkO
  • 15,880
  • 12
  • 60
  • 88
  • I don't know any way to "replace" the package name in the manifest at build time. But what about getting an AndroidManifest file for each flavor ? – Andros Jan 14 '14 at 09:03
  • I considered that but i will have to hardcode the package name everywhere, it will be my second choice if i cannot find a way to read the package name from build.gradle. – DArkO Jan 14 '14 at 10:40

4 Answers4

70

Gradle Plugin v0.12 and higher:

Use ${applicationId} instead of ${packageName}.

Gradle Plugin v0.11 and higher:

As of v0.11, you no longer need to specify not to use the old manifest merger.

Gradle Plugin v0.10 and higher:

Assuming you're using version 0.10 or higher, this is now officially supported:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        // Make sure this is at least 0.10.+
        classpath 'com.android.tools.build:gradle:0.10.+'
    }
}

As of v0.10, you'll also have to manually enable the new manifest merger, although I'd expect that requirement to go away in a version or two whenever the new merger becomes the default:

android {
    useOldManifestMerger false
}

Then, just use ${packageName} anywhere in AndroidManifest.xml that you would normally hardcode the package name. For example:

<category android:name="my.package.name"/>

would become

<category android:name="${packageName}"/>


Gradle Plugin v0.9 and below:

So, referencing this post, it appears this is not yet officially supported through Gradle. A simple workaround is the following:

  1. Replace the package name with a custom tag (e.g. <category android:name="my.package.name"/> becomes <category android:name="_PACKAGENAME_"/>
  2. Add the following to your build.gradle, under the android scope:

applicationVariants.all { variant ->
    // After processing the manifest, replace all instances of your tag
    // with the variant's actual package name.
    variant.processManifest << {
        def manifestOutFile = variant.processManifest.manifestOutputFile
        def newFileContents = manifestOutFile.getText('UTF-8').replace("_PACKAGENAME_", variant.packageName)
        manifestOutFile.write(newFileContents, 'UTF-8')
    }
}
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
3

To do something like this, I use buildTypes in my gradle file but I am pretty sure this will work with flavours as well. For me I am trying to set the label field in the activities.

I have a strings xml file for each of my buildTypes. Then I have a sourceSet for each buildType which includes the correct strings file. Then in the manifest I do not use a hard coded string but rather "@string/my_var" which will pull the correct string depending on how the sourceSets are defined.

This google+ post and related gist may help.

Something else to do is to put a AndroidManifest.xml file into the src/flavour which only contains the bits which are relevant to each flavour. Then take those bits out of the main manifest file. At build time the Manifest files will be merged into one file. You can see the result all of the merged manifests in build/manifests.

maiatoday
  • 795
  • 2
  • 7
  • 15
  • 1
    Also something to note, the packageName only changes the identification in the Manifest file but not the actual java package hierarchy. So in my builds things have different package names so that they are identified correctly on play but in the code it is still using the same package name for the classes etc. – maiatoday Jan 14 '14 at 13:40
2

I had the same problem and implemented a placeholder replace method in Gradle. It does exactly what you'd expect but also takes care about packageNameSuffix attributes so you can have debug and release as well as any other custom builds on the same device.

applicationVariants.all { variant ->
    def flavor = variant.productFlavors.get(0)
    def buildType = variant.buildType
    variant.processManifest.doLast {
        println '################# Adding Package Names to Manifest #######################'
        replaceInManifest(variant,
            'PACKAGE_NAME',
            [flavor.packageName, buildType.packageNameSuffix].findAll().join()) // ignores null
    }
}

def replaceInManifest(variant, fromString, toString) {
    def flavor = variant.productFlavors.get(0)
    def buildtype = variant.buildType
    def manifestFile = "$buildDir/manifests/${flavor.name}/${buildtype.name}/AndroidManifest.xml"
    def updatedContent = new File(manifestFile).getText('UTF-8').replaceAll(fromString, toString)
    new File(manifestFile).write(updatedContent, 'UTF-8')
}

I have it up on a gist too if you want to see if it evolves later.

I found to be a more elegant approach than the multiple resources and XML parsing approaches.

Saad Farooq
  • 13,172
  • 10
  • 68
  • 94
0

Option Gradle:

Use grade attributes API. Some thing like this

manifest.attributes(["attr1":"value1", "attr2":"value2"])

Option 1

How about converting your project to Android - library project, and making extra project for each company. Than you can edit the Manifest file as you wish.

Option 2

Write a batch file.

Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
  • No it should be a simple string replacement. i don't want to restructure the whole project hierarchy not to use Gradle. Even with Gradle i can completely replace the manifest file. – DArkO Jan 14 '14 at 08:50
  • @DArkO What is your laziness level considering option 2? – Ilya Gazman Jan 14 '14 at 08:55
  • I want to do it with Gradle, What you are suggesting is working around a build system that is meant to do these things. – DArkO Jan 14 '14 at 08:56
  • @DArkO I just been looking at Gradle. Before saw it before. Looks cool. How about the Gradle option? – Ilya Gazman Jan 14 '14 at 09:03