Using a different manifestPlaceholder for nested Flavors - android

I do have a structure like below. I wanna use different manifestPlaceHolder like this, projectXTest, projectXDev, projectXProd all use different manifestPlaceHolder while projectYTest,Prod,Dev uses the same manifestPlaceHolder. What I can do besides putting that values to string.xml for all different flavors
android {
buildTypes{
debug{
// Some debug setup
}
release{
// Some release setup
}
}
flavorDimensions "project" , "default"
productFlavors {
projectX{
dimension 'project'
}
projectY{
dimension 'project'
}
Test{
dimension 'default'
}
Dev{
dimension 'default'
}
Prod{
dimension 'default'
}
}
}

Since I could find an out of this. I followed this steps. I hope, If somebody find her/himself in this situation, this helps.
Firstly
Put a manifest to desired flavors directory, for my example I've put android manifest to src/projectY and src/projectX
Beware these not to be full manifest. It should be layered like below
<manifest>
<application>
<activity>
android:name=".x.loginActivity"
tools:node="merge" // this is must
// your flavor spesific code
</activity>
</application>
</manifest>
Secondly
Delete your code to minimum in main XML. What i mean that manifest you've created in src/flavor will be merged with main one. so that delete what you want to seperate and leave what you want to used common in main.
Thirdly
Give different manifestPlaceholder name if you need to seperate prod,dev and staging.
For example,
src/projectX/manifest.xml
<activity>
.
.
android:host='${projectXHost}'
.
</activity>
src/projectY/manifest.xml
<activity>
.
.
android:host='${projectYHost}'
.
</activity>
in gradle
productFlavors{
prod {
manifestPlaceholders = [projectXHost : 'xxx' , projectYHost : 'yyy']
}
}
Since this is my first answer, I'm sorry with layout and expressions. I hope you can understand what I mean. Have a good day

Related

Conditionally include meta-data in Android manifest file

Is there way to conditionally include meta-data element in Android manifest file based on value set in Gradle. I am able to do following (using resValue to set <some_value> in build.gradle) but haven't been able to find way to include/exclude complete meta-data element.
<meta-data
android:name="<some_setting>"
android:value="#string/<some_value>" />
Somewhat of a hack but only way I've been able to address this is by having something like following (have changed names in example) in build.gradle (am using this in context of having different build flavors as well but isn't dependent on that)...I think there's probably some additional smarts that can be done around manifest merging that would make this a bit cleaner as well. myFlag is coming from a project.hasProperty() value I'm passing in as part of Fastlane script
sourceSets {
flavor1 {
if (myFlag == "true") {
manifest.srcFile "src/flavor1_flag/AndroidManifest.xml"
} else {
manifest.srcFile "src/flavor1/AndroidManifest.xml"
}
}
}
<meta-data android:name="string"
android:resource="resource specification"
android:value="string" />

Installing APKs created from single source

This is a follow up question. You don't need to, but you can read the first question.
Here is my problem. I created two apks from a single source. Then I published both APKs on the Google Play Store, each as independent apps. Say one app is named Cat and one is named Dog. Now I am having a problem such that if I install Cat on my device, then I cannot install Dog, and vice versa. Any idea why this might be? The error code from the PlayStore is `Error Code: -505).
EDIT: Replying to responses
I thought the suffixes would take care of that. How might I address that problem and still use a single source code for my two APKs?
productFlavors{
training{
applicationIdSuffix ".training"
versionNameSuffix "-training"
}
production{
// applicationIdSuffix ""
// versionNameSuffix ""
}
}
EDIT 2:
Adding an extra manifest for the .training flavor, resulted in the following error
/Users/myname/StudioProjects/appname/app/src/training/AndroidManifest.xml Error:
Overlay manifest:package attribute declared at AndroidManifest.xml:2:11-50 value=(com.compname.appname.training)
has a different value=(com.compname.appname.training) declared in main manifest at AndroidManifest.xml:2:11-41
Suggestion: remove the overlay declaration at AndroidManifest.xml and place it in the build.gradle:
flavorName {
applicationId = "com.compname.appname.training"
}
Where would this flavorName go exactly?
In your build.gradle,
android.productFlavors {
dog {
applicationId "com.example.dog"
}
cat {
applicationId "com.example.cat"
}
}
Now you can do,
$ gradle assembleDogDebug
or
$ gradle assembleCatDebug
etc.
This would happen because both APKs are for same package.

Change string resource by flavor / debug-build

Let's say we have strings_test.xml, which stores string values for testing and should be shown in a debug-release. When the apk gets build as a release version all values should be change to an empty string e.g. <string name="test_some_card_text">#string/empty</string>.
Is there a possibility to achieve this?
As always, thanks in advance.
Yes you can do that inside your app gradle under buildTypes..
buildTypes {
mybuild {
resValue "string", "test_some_card_text", '"test"'
resValue "string", "other_text", '"other"'
}
debug {
resValue "string", "test_some_card_text", '"test"'
resValue "string", "other_text", '"other"'
}
}
Then access it like this.
getApplicationContext().getResources().getString(R.string.test_some_card_text);
getApplicationContext().getResources().getString(R.string.other_text);
For build you need to select that build variants and have to build it.
Yes, Gradle lets you override strings.
Add this inside buildTypes{} in your app/build.gradle
debug {
applicationIdSuffix "debug"
}
That should create a directory titled debug next to main. If not then manually create one. (Seriously, I haven't tried this, but I know this is possible.)
Then if your strings_test.xml is under res/values, create similar directory structure under debug/ and put your strings_text.xml with debug specific strings there. This will show up in your debug build. The ones under release/main/res/values will show up in your release build.
PS: You can override all res and asset data like this according to buildTypes and flavor. You can't override Java files though, you could however add them.
As #Aditya Naik said it is possible using Flavors.
Official doc says
BuildType -> Flavor -> main -> Dependencies.
This means that if a resource is declared in both the Build Type and in main, the one from Build Type will be selected.
Note that for the scope of the merging, resources of the same (type, name) but different qualifiers are handled separately.
This means that if src/main/res has
res/layout/foo.xml
res/layout-land/foo.xml
and src/debug/res has
res/layout/foo.xml
Then the merged resource folder will contain the default foo.xml from src/debug/res but the landscape version from src/main/res
for more info visit Official doc - Resource Merging
It is not possible to change the string value after creation of the apk.
But you can assing the value to text or edittext ... etx dynamically after creation of the apk.
For those who come here looking for some way to apply a similar method to raw resources, I dealt with it using buildConfigField.
gradle
...
buildTypes {
debug {
...
buildConfigField "int", "shared_resource_name", 'R.raw.debug_resource_name'
...
}
prod {
...
buildConfigField "int", "shared_resource_name", 'R.raw.prod_resource_name'
...
}
}
Pay attention to the quotes. After that, place BuildConfig.shared_resource_name in the files wherever R.raw.resource_value used to be accessed directly.
This can be used to other resources I think.

Application name as gradle parameter

I want to do something like that: gradle build DemoApp, and have DemoApp.apk with application name "DemoApp" as output.
Also, can I change application icon if with the same flow?
And some inner parameters?
Product flavors - not a secret for me. I do not need different configurations, I need to change application name at the build time. For example, I have some Rest API, that allows to pass parameter and return apk with application named as that parameter. The same with icon and other.
Checkout Manifest Merger specifically placeholders. Below is an example of setting the app name in gradle file.
// AndroidManifest.xml
<application
android:label="${applicationName}"
// build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: "MobileWorkforce"]
}
The other option is to use #string/app_name and define different string.xml files based upon buildType/flavor.
Given that you don't know how to use flavours properly, a mix of other solutions.
AndroidManifest.xml
<application
android:label="${applicationName}"
MyHttpThing.java
callserver(BuildConfig.ENDPOINT + "/api/v3/", "stuff")
build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: myappname]
buildConfigField "String", "ENDPOINT", myendpoint
}
and call with
gradle -Pmyappname=namy_name_name -Pmyendpoint="http://google.com" build DemoApp
Looks like you're looking for Product Flavors, they're exactly for having multiple customized versions of the app with shared codebase. You can customize the icon, as long as any other resource. And yes, the .apk will be named according to the name of the flavor.

Android Gradle replace Package name for a value in manifest

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?
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:
Replace the package name with a custom tag (e.g. <category android:name="my.package.name"/> becomes <category android:name="_PACKAGENAME_"/>
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')
}
}
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.
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.
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.

Categories

Resources