I need your help. I wish to add the build name of my app to the gradle file. The thing is I do NOT want it to be visible to user on the market, so this:
versionName "3.1.0.5486"
does not suits me. But I need it in a way so I can read it via code (In the about screen for example) and increment it each build. How can you advise me?
Just use standard versionCode parameter. You can read it from code using
BuildConfig.VERSION_CODE.
You can set the version code and name inside the defaultConfig element in the build.gradle:
defaultConfig {
versionCode 1
versionName "1.0"
}
And use it as is:
String.format(getString(R.string.some_text_with_int_placeholder), BuildConfig.VERSION_CODE);
If you want create your own variable just use buildConfigField, something like:
buildTypes {
debug {
buildConfigField "int", "FOO", "42"
buildConfigField "String", "FOO_STRING", "\"foo\""
buildConfigField "boolean", "LOG", "true"
}
}
And access it via BuildConfig.FOO
Related
I'm dividing my app into multiple flavors, some of them should have HMS and some of them should not, so far I've managed to sucessfully divide source code but I can't get this part of gradle config to work as I expect it:
if(rootProject.ext.useHuaweiLib == true) {
implementation 'com.huawei.agconnect:agconnect-core:1.4.1.300'
implementation 'com.huawei.hms:push:5.0.1.300'
}
Here I'm adding libraries only if useHuaweiLib is true, here is flavor division in app module:
flavorDimensions "purpose"
productFlavors {
client {
dimension "purpose"
buildConfigField "boolean", "USE_HUAWEI_LIB", "false"
}
defaultConfig {
dimension "purpose"
buildConfigField "boolean", "USE_HUAWEI_LIB", "false"
}
huawei_client{
dimension "purpose"
buildConfigField "boolean", "USE_HUAWEI_LIB", "true"
rootProject.ext.useHuaweiLib = true
}
sdk {
dimension "purpose"
buildConfigField "boolean", "USE_HUAWEI_LIB", "false"
}
}
and variable declaration in root module:
ext {
buildToolsVersion = "32.0.0"
compileSdkVersion = 31
minSdkVersion = 23
targetSdkVersion = 30
versionCode = 1
releaseName = "1.0"
versionName = "1.0.0"
useHuaweiLib = false
}
As you can see the variable is false by default and is only set to true in huawei_client flavor, but for some reason even if I choose 'client' flavor I still get access
to HMS api. I want to be able to select a flavors with different set of sources and libs.
You can add the flavor as a prefix to all dependency imports. Ditch the if statement and just use this:
huawei_clientImplementation 'com.huawei.agconnect:agconnect-core:1.4.1.300'
huawei_clientImplementation 'com.huawei.hms:push:5.0.1.300'
This way, the two librarys will only be added to the huawei_client flavor.
As you know, gradle stopped (official documentation) support VERSION_CODE and VERSION_NAME in the Gradle DSL. For more info click hire. So, I added this code (app build.gradle):
defaultConfig {
versionCode 1
versionName "1.0"
buildConfigField 'int', 'VERSION_CODE', "1" //fix issue
buildConfigField 'String', 'VERSION_NAME', "\"1.0\"" //fix issue
}
//..
buildTypes {
debugToast{
versionNameSuffix "-debug(Toasts)" //NOW DOES NOT WORK !!
}
}
//..
And my question: How fix versionNameSuffix method, because it doesn't work?
I'd like to have different configurations for debug and release versions. For the most part, By configuration I mean having different string constants,
e.g. connection strings. Additionally I'd like to have a running configuration to be connected to a build configuration, so that when I select 'release' from the running dropdown, the correct version is automatically built. Is that even possible? Is there a way to use a different string resource file based on build configuration?
There is a product flavors functionality available in android studio. You have to add different flavors for your application in the app level build.gradle file. You can set them as follows:
productFlavors {
sandbox {
versionCode 1
versionName "1.0"
applicationId "com.abc.sandbox"
buildConfigField 'String', 'HOST', '"http://api/v1/"'
}
development {
versionCode 1
versionName "1.0"
applicationId "com.abc.development"
buildConfigField 'String', 'HOST', '"http://api/v1/"'
}
production {
versionCode 1
versionName "1.0"
applicationId "com.abc.production"
buildConfigField 'String', 'HOST', '"http://api/v1/"'
}
}
You can run respective flavor by selecting it from build versions before running your application.
You can create a separate strings.xml for debug mode and add your strings to it.
Right click res folder in your projects direcotry -> new -> Android Resource File.
In the new window, Enter filename (strings.xml or any other file you need), select debug in Source Set as shown in image, click OK.
Add your required data here in this new file as,
<string name="same_key_as_in_original">Value</string>
Hi I'm trying to implement android app flavor (free and full) to live wallpaper. In eclipse, I used to use this following code to open live wallpaper preview from my own android Activity:
Intent intent = new Intent();
intent.setAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
String pkg = WallpaperService.class.getPackage()
.getName();
String cls = WallpaperService.class.getCanonicalName();
intent.putExtra(
WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
new ComponentName(pkg, cls));
But now it does not work correctly as the free and full flavor are using same package name with just different applicationId in android studio. The problem is when it starts either in free or full version, it will goto full version no matter how, regardless of what flavor it is. I specify app flavor using applicationId in project gradle like this:
productFlavors {
free {
applicationId "com.kkl.app.free"
}
full {
applicationId "com.kkl.app"
}
}
How do we make it to get the correct package name that matches the app flavor?
You can call getPackageName() in your Activity to get Android packageName. This will be packageName from manifest file i.e. the one equal to current applicationId.
Method documentation can be found here.
Fixed by using the following:
intent.setAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
String pkg = getPackageName();
String cls = WallpaperService.class.getCanonicalName();
intent.putExtra(
WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
new ComponentName(pkg, cls));
Special thank to Lingviston
You can do a lot with flavors, but what you are trying to do is far simpler than anyone has answered.
First you have a build variant to select your flavor for debugging and running. So use this, otherwise all your debugging will use default main release.
Secondly, you don't have to get package name, just use a build config flag or check flavor. I.E.
android {
signingConfigs {
releaseA35Demo {
storeFile file("$projectDir/../yaskeystore.jks")
storePassword System.getenv('YOUR_APP_STUDIO_STORE_PASSWORD')
keyAlias System.getenv('YOUR_APP_STUDIO_KEY_ALIAS')
keyPassword System.getenv('YOUR_APP_STUDIO_KEY_PASSWORD')
}
}
flavorDimensions 'default'
productFlavors {
a35Demo {
dimension 'default'
applicationId "com.appstudio35.yourappstudio"
buildConfigField "String", "SERVER_URL", '"http://fakeNumbers.compute-1.amazonaws.com:3006"'
buildConfigField "int", "BUSINESS_ID", "1"
versionCode 1
versionName "0.01.01-b1"
minSdkVersion 21
}
a35DemoDev {
dimension 'default'
applicationId "com.appstudio35.yourappstudio.dev"
buildConfigField "String", "SERVER_URL", '"http://fakeNumbers2.compute-1.amazonaws.com:3006"'
buildConfigField "int", "BUSINESS_ID", "2"
versionCode 1
versionName "0.01.01-b1"
minSdkVersion 21
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
productFlavors.a35Demo.signingConfig signingConfigs.releaseA35Demo
productFlavors.a35DemoDev.signingConfig signingConfigs.releaseA35Demo
}
}
}
Then simply reference it in code like:
BuildConfig.BUSINESS_ID
Wherever you need it. Just make sure you don't accidentally use the BuildConfig of a library project when it auto imports the BuildConfig.
Next way is if you want to check your flavor you can simply do
BuildConfig.FLAVOR to see which one you are on. However, keep in mind there are some compiler warnings about using it because you are checking against a flavor and the BuildConfig assumes it will ALWAYS be whatever you are currently in for the Build Variant dropdown, Which is not true, you can ignore this always true or always false warning, I assure you it works.
Lastly your package issue is just because you are debugging the wrong build variant. I'll add an image so you can see where to change that.
Hope that helps.
I used to have the following project flavors:
Apple
Orange
Originally the only difference was the applicationId/packageName. Now there is a single java file that is different. A custom ArrayAdapter to be exact. The solution was to create src/Apple and src/Orange and both inherit from src/main. I removed the java file from src/main and put a copy into src/Apple and src/Orange and modified it appropriately. All was good in the world.
Fast forward a few weeks, now there are about 10 java files that differ between Apple and Orange. Again... no big deal. Easy to handle. Separate java files in src/Apple and src/Orange.
Fast forward to today. I need to modify things up a bit, because I want to have a free and premium version of each. The free and premium versions only differ by a URL. I was going to simply create the new types called:
AppleFree
ApplePremium
OrangeFree
OrangePremium
I have a dilema though. Since now src/Apple and src/Orange have 10 different files that have been changed... if I change any java file in AppleFree I have to make sure I do the same in ApplePremium. I'm kind of at a crossroads and hope my question makes sense at this point. I have come up with three possible solutions, but I'm not sure how I would implement them/what would be the correct approach/the solution is not what I want.
Solution 1:
Use an if statement
if (BuildConfig.FLAVOR==appleFree) {//use free Url} else {// use premium url}
Issue: Both Urls are technically compiled into the apk. I do not want this.
Solution 2:
Have src/AppleFree and src/ApplePremium inherit from an src/Apple parent directory somehow.
Issue: Not sure how I would do this.
Solution 3:
Add the free and premium url right in build.gradle like so?
productFlavors {
appleFree {
applicationId "com.example.apple.free"
versionName "1.0"
url "http://freeurl.com"
versionCode 1
}
applePremium {
applicationId "com.example.apple.premium"
versionName "1.0"
url "http://premiumurl.com"
versionCode 1
}
orangeFree {
applicationId "com.example.orange.free"
versionName "1.0"
versionCode 1
url "http://freeurl.com"
}
orangePremium {
applicationId "com.example.orange.premium"
url "http://premiumurl.com"
versionName "1.0"
versionCode 1
}
}
Issue: Not sure how to make that work.
Any tips are helpful.
EDIT:
Final Solution?
flavorGroups 'fruit', 'paid'
productFlavors {
apple {
flavorGroup 'fruit'
}
orange {
flavorGroup 'fruit'
}
free {
flavorGroup 'paid'
}
premium {
flavorGroup 'paid'
}
appleFree {
applicationId "com.example.apple.free"
versionName "1.0"
buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
versionCode 1
}
applePremium {
applicationId "com.example.apple.premium"
versionName "1.0"
buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com'
versionCode 1
}
orangeFree {
applicationId "com.example.orange.free"
versionName "1.0"
versionCode 1
buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
}
orangePremium {
applicationId "com.example.orange.premium"
buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com'
versionName "1.0"
versionCode 1
}
}
There are many possible solutions to your problem. The most native-Gradle solution would be to use Flavor Dimensions, as documented in http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants
This is also similar to what you were thinking about with Solution 2.
It would work something like this:
flavorDimensions 'fruit', 'paid'
productFlavors {
apple {
dimension 'fruit'
}
orange {
dimension 'fruit'
}
free {
dimension 'paid'
}
premium {
dimension 'paid'
}
}
This will give you build variants (and source folders) where it does the combination of all possibilities out of each flavor dimension, maintaining the same order as how the groups are specified in your flavorDimensions statement (i.e. it's appleFree, not freeApple), thus:
* appleFree
* applePremium
* orangeFree
* orangePremium
in your src/ folder, you can have these possibilities:
* src/main
* src/apple
* src/orange
* src/free
* src/premium
* src/appleFree
* src/applePremium
* src/orangeFree
* src/orangePremium
Solution 3
You can use the buildConfigField to specify constants that go in the BuildConfig class on a flavor-by-flavor basis:
productFlavors {
appleFree {
buildConfigField 'String', 'MY_URL', 'value1'
}
applePremium {
buildConfigField 'String', 'MY_URL', 'value2'
}
orangeFree {
buildConfigField 'String', 'MY_URL', 'value3'
}
orangePremium {
buildConfigField 'String', 'MY_URL', 'value4'
}
Solution 1
I was trying to work up something along the lines of Solution 1, but it won't work well for your exact use case. If you have an if condition in Java that tests against a boolean that's declared static final then the compiler can determine statically whether the code is reachable or not, and it will strip it if it's not. Thus:
static final boolean DEBUG = false;
...
if (DEBUG) {
// do something
}
The code in // do something won't get compiled at all. This is an intentional and documented behavior on the part of the Java compiler, and allows you to write expensive debug code that won't get compiled into your release binary. BuildConfig.DEBUG is declared as static final for this exact reason.
There's a BuildConfig.FLAVOR, but it's defined as String, and you don't get the same benefit:
static final String FLAVOR = "orange";
...
if (FLAVOR.equals("apple")) {
// do something
}
The compiler isn't smart enough to do static analysis, see that // do something is unreachable, and not compile it. Note that it will work fine at runtime, but that dead code will be included in your binary.
If it suits you, though, you could steal the buildConfigField approach from above and define an extra boolean variable in some variants that could allow code to be conditionally compiled in. This is more complex than defining the string directly as in Solution 3, but if you find yourself wanting to differentiate behavior without going through the trouble of making flavor-specific subclasses, you could go this route.
Here's how I implemented product flavors inheritance.
stageLt (extends baseLt) - app for Lithuania using stage API
productionLt (extends baseLt) - app for Lithuania using production API
stagePl (extends basePl) - app for Poland using stage API
stagePl (extends basePl) - app for Poland using production API
android {
flavorDimensions "default"
productFlavors {
def API_URL = "API_URL"
def PHONE_NUMBER_PREFIX = "PHONE_NUMBER_PREFIX"
def IBAN_HINT = "IBAN_HINT"
baseLt {
buildConfigField "String", PHONE_NUMBER_PREFIX, '"+370"'
resValue "string", IBAN_HINT, "LT00 0000 0000 0000 0000"
}
basePl {
buildConfigField "String", PHONE_NUMBER_PREFIX, '"+48"'
resValue "string", IBAN_HINT, "PL00 0000 0000 0000 0000 0000 0000"
}
stageLt {
dimension "default"
applicationId "lt.app.stage"
buildConfigField "String", API_URL, '"https://www.limetorrents.pro/"'
}
productionLt {
dimension "default"
applicationId "lt.app"
buildConfigField "String", API_URL, '"https://yts.mx/"'
}
stagePl {
dimension "default"
applicationId "pl.app.stage"
buildConfigField "String", API_URL, '"https://1337x.to/"'
}
productionPl {
dimension "default"
applicationId "pl.app"
buildConfigField "String", API_URL, '"http://programming-motherfucker.com/"'
}
}
// base product flavors will not show up in 'Build Variants' view.
variantFilter { variant ->
if (variant.name.startsWith("base")) {
setIgnore(true)
}
}
}
void setupProductFlavor(baseFlavor, flavor) {
flavor.buildConfigFields.putAll(baseFlavor.buildConfigFields)
flavor.resValues.putAll(baseFlavor.resValues)
flavor.manifestPlaceholders.putAll(baseFlavor.manifestPlaceholders)
// Note that other product flavor properties ('proguardFiles', 'signingConfig', etc.) are not merged.
// E.g. if in base product flavor you declare 'signingConfig', it will not be copied to child product flavor.
// Implement if needed.
}
// Merge 'parent product flavors' with 'child product flavors'
setupProductFlavor(android.productFlavors.baseLt, android.productFlavors.stageLt)
setupProductFlavor(android.productFlavors.baseLt, android.productFlavors.productionLt)
setupProductFlavor(android.productFlavors.basePl, android.productFlavors.stagePl)
setupProductFlavor(android.productFlavors.basePl, android.productFlavors.productionPl)