Different API Host with gradle and android - android

I was wondering if it is possible to provide a different API Host per build using gradle. Ideally I would like to access the constant through my code the same so when I do a gradle build, it builds the release.apk to point to http://example.com and the debug.apk to point to http://debug.example.com.
I have achieved this using the following:
buildTypes {
debug {
buildConfig "public final static String API_HOST = \"http://debug.example.com\";"
}
release {
buildConfig "public final static String API_HOST = \"https://example.com\";"
}
}
However that seems pretty dirty
Cheers

I think a better alternative with today's Gradle features is to specify both productFlavors and buildTypes (example below).
The buildTypes control what certificate I sign with, and whether Proguard is run.
The productFlavors control the intended environment, which includes custom resources, as well as a different package name so I can install them both side by side.
Then I configure my server address in strings.xml for each variant and load it at runtime.
src/dev/res/values/strings.xml
src/staging/res/values/strings.xml
strings.xml example from the "dev" variant:
<string name="config_url">http://com.example.debug</string>
build.gradle snippet:
productFlavors {
dev {
packageName "com.example.dev"
}
staging {
packageName "com.example.staging"
}
}
buildTypes {
debug {
versionNameSuffix " debug"
signingConfig signingConfigs.debug
}
release {
// A release build runs Proguard, and signs with a release certificate
zipAlign true
runProguard true
proguardFile 'proguard-project.txt'
proguardFile getDefaultProguardFile('proguard-android-optimize.txt')
signingConfig signingConfigs.release
}
}

An alternative is to use buildConfigField:
buildTypes {
debug {
buildConfigField 'String', 'API_HOST', '"https://debug.example.com"'
}
release {
buildConfigField 'String', 'API_HOST', '"https://example.com"'
}
}
You would then refer to it in your code via BuildConfig.API_HOST

So I ended up speaking to one of the gradleware engineers about this.... My initial solution is the correct way. Google/Gradle will be improving this in the future.
To add multiple values you separate the the strings with comas.

Related

How to set up different Constants in Kotlin for environment

We have an App in Kotlin ( Android Stdio) which has different constants by environment.
We are using Constants.kt
const val IMAGES_API = "https://localhost:3000/v1/images"
and we want to use the same variable in staging/qa/prod.
The App is building in Kotlin and we are using gradle (groovy scripts) to compiling and packing the different environment staging/qa/prod.
My first approach has been to create this properties on the gradle.properties and load the properties on the build.gradle file like this :
def loadProperties() {
def props = new Properties()
file("gradle.properties").withInputStream { props.load(it) }
def config = props
project.ext.config = config
}
And when I run gradle I can see the new properties, but I don't know how to get this value inside the App ( in the kotlin code).
My only idea is to create a task on build.gradle to copy a Constants.kt file by environment. But, I don't think, it's a good practice. I think, there must be another way to set different variables in the App.
Please, can anybody help me with this?
What you want is to configure build types in your app module's gradle file with buildConfigField in each:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
debug {
applicationIdSuffix ".debug"
debuggable true
buildConfigField "String", "SERVER_URL", '"http://test.this-is-so-fake.com"'
}
/**
* The `initWith` property allows you to copy configurations from other build types,
* then configure only the settings you want to change. This one copies the debug build
* type, and then changes the application ID.
*/
staging {
initWith debug
applicationIdSuffix ".debugStaging"
buildConfigField "String", "SERVER_URL", '"http://prod.this-is-so-fake.com"'
}
}
In code, you can refer to BuildConfig.SERVER_URL, and it will be populated with the string based on the build type you choose at compile time.
You can build different apk/app bundles to distribute.
Referencing this answer .
EDIT As an aside, in real life I have found this approach to be... annoying. It is easier to bundle inside the app a toggle that allows QA to switch between environments. This way you only have one bundle to deal with.

Android Studio does not show all build variants

Below are the buildTypes and flavors parts of my build.gradle:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
}
}
flavorDimensions "default"
productFlavors {
free {
android.sourceSets.free.setRoot('src/main')
dimension "default"
}
plus {
applicationIdSuffix '.plus'
versionName '1.0'
android.sourceSets.plus.setRoot('src/plus')
dimension "default"
}
}
Android Studio only shows two build variants (freeDebug and freeRelease) in the Build variants window. It does not show plusDebug or plusRelease. I have another project with a similar build.gradle and I can clearly see four build variants. Any ideas where I should look?
plus is a default method in groovy. It's not a bug in Android Studio or anything else. You are executing this function in DefaultGroovyMethods
public static <T> Set<T> plus(Set<T> left, T right) {
return (Set)plus((Collection)left, (Object)right);
}
This is because the delegate passed into productFlavors implements Set.
See productFlavors definition
This appears to be a bug/limitation in Gradle. I have filed an issue for it.
Use something else, other than plus. I tried quoting it ("plus"), thinking that perhaps it's a conflict with a keyword, but that had no effect. But Plus and plussss and phat all work.

Android white labeling

I am working for a company which has this "foo" app on the store, the app is a helper for our hardware which is revelled by resellers. We made sure that the name is as generic as possible - in order for our vendors to be able to market the app as "their app".
However - some re-sellers do want to have their exact name and icon on the app. And they are willing to pay so, I need to make this happen.
Questions:
I understand that I am looking for build variants. But still, how can I modify the apk-package-name , display name is default launcher icon using build variants?
Is this permitted...? I am not "officially" spamming the store, but it feels like I could get banned for doing that exactly.
Code signing - I will upload the APK my self, and I will need to sign using different certificates (whatever it's called on android). Again - this is vague, and I cannot find documentation on this subject.
I also plan on releasing a beta version of my app in this way. I am currently using the standard mechanism, but this means that testers cannot show case the app to customers (as it's not finished or crashing most of the time) [1]
Does the term "white labeling" apply here...?
[1] the joys of working in a small company :)
You can do this with build variants as you suspected but also you would likely need Flavors.
Here is an example gradle file that has multiple build types and flavors. You set the ApplicationId (packagename used in Play Store) in the flavor settings.
Set up the signing in each type/flavor. You can add resources, icons, manifests etc that are specific to each flavor. You can even replace whole class files so customer specific code is only included in the apk for the customer you are building for.
defaultConfig {
applicationId "uk.co.foo.default"
minSdkVersion 14
targetSdkVersion 23
versionCode = 113
versionName = "3.2.3"
}
signingConfigs {
release {
storeFile file("X:\\Android Projects\\Keystore\\MyKeys.jks")
storePassword "MyPassword"
keyAlias "KeyAlias"
keyPassword "itsasecret"
}
}
productFlavors {
Customer1 {
applicationId "uk.co.foo.customer1"
}
Customer2 {
applicationId "uk.co.foo.customer2"
}
}
buildTypes {
debug {
applicationIdSuffix ".debug"
versionNameSuffix " Debug"
}
beta {
applicationIdSuffix ".beta"
versionNameSuffix " Beta"
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
signed {
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
Here is the folder structure for adding resources for each type\flavor. In this example, the second flavor is called "fan". The "main" folder is used by default. Each type and flavors resources are merged into the apk (replacing any of the same name in the main folder) depending on which build you choose in the "Build Variants" section of Android Studio.
Android Studio will display which folders are in effect for the current build as shown highlighted in this image.
Edit - full official documentation is available here: https://developer.android.com/tools/building/configuring-gradle.html

using getBuildConfigFields().getAt() in build.gradle returns null

In my android build.gradle file, I want to use a build config field value that is set in each flavor for my package name suffix.
Say my package is "com.my.app", and my config field's name is "myConfigFieldName".
I try to do:
applicationIdSuffix "." + getBuildConfigFields().getAt("myConfigFieldName")
And after running the build, my package is "com.my.app.null", when I expected something like "com.my.app.flavorA"
Is there a way to achieve what I'm trying to do?
OK, so this is a way to append the flavor name onto the app package.
I also check that the release type is release, so I will be able to switch between flavors during dev without installing more and more apps
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
applicationIdSuffix ".debug"
}
//append flavor name to the package name in release builds
applicationVariants.all { variant ->
if(variant.buildType.name.equals('release')) {
variant.productFlavors.each() { flavor ->
variant.mergedFlavor.applicationId = variant.mergedFlavor.applicationId + '.' + flavor.name
}
}
}
}

Build android with gradle, replace string each product flavor

Before i build android project to two different application paid and free.
I changed each values and strings so yesterday I made a big mistake.
So, I'm laerning how to use gradle to build my app.
My app have some differents.
app name (just add suffix '-Free') -> values/string.xml
change flag in my *.java
// signingConfigs is ommited.
productFlavors{
free{
packageName "my.app.free"
versionCode 20
signingConfig signingConfigs.freeConfing
copy{
from('/res'){
include '**/*.xml'
}
into 'build/res/'
filter{
String line -> line.replaceAll("android:label=\"#string/app_name\"", "android:label=\"#string/app_name_free\"")
}
}
copy{
from('/src'){
include '**/*.java'
}
into 'build/src/'
filter{
String line -> line.replaceAll("public static final Boolean IS_FULL_VER = true;", "public static final Boolean IS_FULL_VER = false;")
}
}
}
paid{
packageName "my.app.paid"
versionCode 20
signingConfig signingConfigs.paidConfing
}
}
but, built app changed nothing at all.
What i missed?
See the documentation on product flavors:
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-flavors
In your build.gradle, in each flavor, you can define flags to be generated in your BuildConfig.java file:
productFlavors {
free {
packageName "com.company.appfree"
buildConfig "public final static com.company.common.MonetizationType monetizationType = com.company.common.MonetizationType.FREE;"
}
paid {
packageName "com.company.apppaid"
buildConfig "public final static com.company.common.MonetizationType monetizationType = com.company.common.MonetizationType.PAID;"
}
}
This example uses an enum (that you need to define somewhere in your java code):
public enum MonetizationType {
PAID, FREE
}
You can now use this anywhere like this:
if (BuildConfig.monetizationType == MonetizationType.FREE) { ... }
For overriding resources, you can create different resource files in the source folders for each flavor:
Use the following structure
app/build.gradle
app/ [.. some other files...]
app/src/main/
app/src/main/java
app/src/main/res
app/src/main/assets
app/src/main/AndroidManifest.xml
app/src/free/res/values/apptitle.xml
app/src/paid/res/values/apptitle.xml
apptitle.xml would be a string resource file (just like strings.xml), but with only one string: the one you want to be different depending on the flavor.
(You don't need have a apptitle.xml in your main/res directory).
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_title">App Title (or whatever you want)</string>
</resources>
You might be able to override strings in different ways, but I like to keep the overridden strings separate from the rest for clarity.
The accepted answer is not working with the newer versions of Gradle.
You need to replace buildConfig with buildConfigField to get the same result:
productFlavors {
free {
packageName "com.company.appfree"
buildConfigField "com.company.common.MonetizationType", "MONETIZATION_TYPE", "company.common.MonetizationType.FREE"
}
paid {
packageName "com.company.apppaid"
buildConfigField "com.company.common.MonetizationType", "MONETIZATION_TYPE", "company.common.MonetizationType.PAID"
}
}
treesAreEverywhere's answer (as well as user name) is right on. But it's also very valuable to know that Android Studio will greatly simplify the process of creating build flavor or attribute specific resource files.
In the AS project window, right click on the res/values folder and select New > Values resource file. Then name it (e.g., "strings"), select the Source set if not the default, and select any desired qualifiers (e.g., Screen Width = 800). This is the easiest way to make sure you're putting your resource overrides where the compiler wants them.
This is how I did in my project. I created multiple build types instead of flavours. This solution will add a prefix to your package name based on what build type you are trying to assemble. For ex, for dev the package name will be com.sample.myapp.dev and similarly for beta package name will be changed to com.sample.myapp.release. You can tweak it to get Free and Paid prefixes. Hope it would help.
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.production
applicationIdSuffix '.release'
versionNameSuffix '-RELEASE'
}
dev {
signingConfig signingConfigs.debug
applicationIdSuffix '.dev'
versionNameSuffix '-DEV'
}
beta {
signingConfig signingConfigs.debug
applicationIdSuffix '.beta'
versionNameSuffix '-BETA'
}
debug {
signingConfig signingConfigs.debug
applicationIdSuffix '.debug'
versionNameSuffix '-DEBUG'
debuggable true
}
}

Categories

Resources