Declare variables in gradle to use in Android project - android

I am reading config fields from an environment.properties file that I would like to reuse in my Android application. I have tried various ways to get values from gradle's build without success (Gradle 2.7):
buildTypes {
debug {
buildConfigField "String", "SERVER_SCHEME", getBuildConfigField('SERVER_SCHEME')
buildConfigField "String", "SERVER_BASE_URL", getBuildConfigField('SERVER_BASE_URL')
buildConfigField "int", "SERVER_PORT", getBuildConfigField('SERVER_PORT')
buildConfigField "String", "SERVER_CONTEXT", getBuildConfigField('SERVER_CONTEXT')
}
}
where getBuildConfigFields(key) is a function defined in my config.gradlereading a file and returning the value corresponding to the specified key.
I have tried :
buildConfigField "TYPE" "KEY" "VALUE",
buildConfigField("TYPE" "KEY" "VALUE")
resValue "TYPE" "KEY" "VALUE"
But none of the are working with gradle errors like :
Error:(71, 0) Gradle DSL method not found: 'buildConfigField()' Possible causes:
The project 'app' may be using a version of Gradle that does not contain the method. Gradle settings
The build file may be missing a Gradle plugin. Apply Gradle plugin
I am using gradle 2.7 to build my android app and found these ways to set BuildConfigFields in different answers like this one but they seem to be outdated for newer versions. What functions can I use to get values from gradle in my application ?
Thank you !

Related

Reach the version code and name from a library module

I'm currently working on modularizing my application, so I'm putting each feature in a library module. In one of my features, I access the version code and name. However, although I defined them in the build.gradle file specific for that module
they were not there in the generated BuildConfig file
I cannot reference the BuildConfig file of the original :app module since I cannot let my feature modules have a dependency on that module.
is there an alternative way of accessing this info from the library module?
Version code and version name are properties of an application package and they are not supported for a library module.
At runtime, you can pass in a Context and access this application package metadata with the help of PackageManager. Example: Detect my app's own android:versionCode at run time
While the answer above is okay, IMO it's not the best way to go about it. Generally speaking, you want your version name and code stored somewhere outside of the build.gradle file. Very often we do that so we can easily access it from the outside of the app, or to update it automatically for CI systems, etc.
A very simple example: You can put those in the gradle.properties file, like so:
<!-- add to the end of the file: -->
VERSION_NAME=0.0.1
VERSION_CODE=1
Then you can simply access them in any build.gradle via properties object, and add them to BuildConfig like so (note: they will only become available after a successful build):
// build.gradle file of the chosen module
def versionName = properties["VERSION_NAME"]
def versionCode = properties["VERSION_CODE"]
buildTypes.all {
buildConfigField "String", "VERSION_NAME", "\"$versionName\"" // Escaping quote marks to convert property to String
buildConfigField "int", "VERSION_CODE", "$versionCode"
}
Alternatively, you can put those in a dedicated version.properties file. Then you can do the same like this:
def versionProps = loadPropertiesFile(project.file('version.properties'))
buildTypes.all {
buildConfigField "String", "VERSION_NAME", getPropEscaped("VERSION_NAME", versionProps)
buildConfigField "int", "VERSION_CODE", getProp("VERSION_CODE", versionProps)
}

Android : resValue variables could not be resolved in Gradle

I am following an android example from vogella.com about Retrofit.
http://www.vogella.com/tutorials/Retrofit/article.html
I get stuck where one is supposed to do the following:
add the stackapp id and key
According to Vogella
After you registered your application, you get a Client Id and a Key . As we want to keep them out of a possible version control system, we import them into our project using gradle. To do so, go to your gradle home directory (.gradle/ in your users home directory) and paste the following lines into your gradle.properties file (create one, if you can’t find it). Of course, replace yourKey and yourClientId with your corresponding values from Stackapps.
I went to this directory and created a new file
gradle.properties
and in that file I added the keys
key=xxxxxxxx
client_id=xxxx
and then in the gradle.properties i added the following in deafultconfig {
resValue("string", "key", project.key)
resValue("string", "client_id", project.client_id)
But both the key and the client_id's could not be resolved.
What could the solution be to this?
Try to change the sintax :
resValue "string", "key", project.key
resValue "string", "client_id", project.client_id
And dont forget to add the variables inside your gradle file:
ext.key = "your_key"
ext.client_id = 0
The third parameter should be a String, and you may should define the key and cliend_id in local.properties and use code below:
Properties lp = new Properties()
lp.load(newInputStreamReader(project.rootProject.file('local.properties').newInputStream(), StandardCharsets.UTF_8))
resValue "string", "key", p.getProperty('key', '0')//default "0"
resValue "string", "client_id", p.getProperty('client_id', '0')

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.

Android Studio - Gradle: How to replace variables in a file

Im fairly new to Gradle and have been using Eclipse and Ant to do all the builds. Within our app we have a config.properties file located in the assets folder at the same level as src and res etc. In this file we have the following:
developmentsettings=true
defaultLogLevel=4
prodEnvironment=false
How can I replace the values of these 3 variables in the build.gradle file? Do I create a new task for this, and if so, is this within the android tag or not?
Any and all help would be must appreciated
I'm not sure if you specifically need these in a separate file... but if not, BuildConfig seems like the way to go. You do something like this in your gradle file:
buildTypes {
debug {
buildConfigField "boolean" "DEVELOPMENT_SETTINGS" "true"
buildConfigField "int" "DEFAULT_LOG_LEVEL" "4"
buildConfigField "boolean" "PROD_ENVIRONMENT" "false"
}
release {
buildConfigField "boolean" "DEVELOPMENT_SETTINGS" "false"
buildConfigField "int" "DEFAULT_LOG_LEVEL" "4"
buildConfigField "boolean" "PROD_ENVIRONMENT" "true"
}
}
And then from your source android code, you reference these fields like so:
BuildConfig.DEVELOPMENT_SETTINGS
BuildConfig.DEFAULT_LOG_LEVEL
BuildConfig.PROD_ENVIRONMENT
You're essentially declaring constants, from gradle, that can be used in your java code.
At build.gradle set the following
android {
buildTypes {
debug{
resValue "string", "app_name", "My App Name Debug"
}
release {
resValue "string", "app_name", "My App Name"
}
}
}
You can access them in the usual way with #string/app_name or R.string.app_name
You can let the build system include different versions of the assets/config.properties file depending on whether you're doing a debug or release build. To do this, don't put the file in src/main/res/assets/config.properties; instead, put the debug version in src/debug/res/assets/config.properties and the release version in src/release/res/assets/config.properties. The build system will pick the right version when it does the corresponding build.

How do you manage multiple environments while developing Android apps?

We're building an Android app that connects to the cloud. We have a test URL for our APIs and a production URL. We connect the app to our local development machines to talk to the database when developing but find ourselves modifying a global API URL to the production URL every time we generate an APk for the Play Store.
Is there a better way to manage environments for Android? Can we also have two versions of the app (development version) and the Play Store version? I am not able to have two versions as both the apps have the same signature. How do we best manage this?
With android studio and gradle its simple now.
inside your app build.gradle edit signing configs
signingConfigs {
debug {
storeFile file("debug.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
prod {
storeFile file("prod.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
dev {
storeFile file("dev.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
}
add buildTypes
buildTypes {
debug {
buildConfigField 'String', 'BASE_URL', '"http://127.0.0.1:8080/"'
......
signingConfig signingConfigs.debug
}
prod {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL', '"http://prod.example.com"'
......
signingConfig signingConfigs.prod
}
dev {
buildConfigField 'String', 'BASE_URL', '"http://dev.example.com"'
......
signingConfig signingConfigs.dev
}
}
In your code take base url configured in gradle file by this code.
public final static String BASE_URL = BuildConfig.BASE_URL;
You can also put different KEY or whatever which is build type specific in gradle file and in code it will take according to the build type you are running.
Its even possible to have different package name.
productFlavors {
my_prod {
applicationId "com.example.packtwo"
}
my_dev {
applicationId "com.example.packone"
}
}
In recent gradle config, there are some updates in specifying package name. You have to add flavourDimensions if using productFlavours. See below code with added flavourDimensions
flavorDimensions "pack"
productFlavors {
flavor_dev {
applicationId 'com.example.packtwo'
dimension "pack"
}
flavor_prod {
applicationId 'com.example.packone'
dimension "pack"
}
}
This will give you more details about product flavours and dimensions
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
Check for more possibilities...
But if you are using different flavors you might have to deal with Manifest merging and all.
This is can be achieved using product flavours.
For achieving this requirement:
First of all, Create 2 files under the app folder of your project say development.props and production.props. Or you can add these 2 files in a package under app folder say config.
Basically, these 2 files contain keys and values. This key is same for both files. But their values are different. These files contain one key say “SERVER_URL” and its value. It would be written like this:
SERVER_URL=”Server_url_value”
In this case, only URL is different. So, I have added only one key-value pair in Props file. You can add more.
Then, create ProductFlavours in the app build.gradle file say development and production. Now, access different props files containing URLs in their correseponding flavours like this:
productFlavors {
development {
getProps('./config/development.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
production {
getProps('./config/production.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
}
def getProps(path) {
Properties props = new Properties()
props.load(new FileInputStream(file(path)))
return props
}
Now, For each flavour, there is a build type And this BuildType is added in app build.gradle. For example, Build type is Debug and release. And I have two flavours i.e. development and production. So, gradle task will be created using both flavour and build type like this:
assemble{flavourName}{BuildType}
Now, you need to type these commands only. It would generate required APK with its corresponding URL. Commands are:
./gradlew assembleProductionRelease would generate release build with Production URL.
./gradlew assembleDevelopmentDebug would generate debug build with Development URL.
./gradlew assembleProductionDebug would generate debug build with Production URL.
./gradlew assembleDevelopmentRelease would generate release build with development URL.
Top three gradle task would be very helpful. But the last task would generate Release build with development URL. But this is not recommended. So, we should stop developer to execute this task i.e. ./gradlew assembleDevelopmentRelease
Now To restrict developer to generate release build using Development URL, add this snippet in your app build.gradle file:
android.variantFilter { variant ->
if(variant.buildType.name.equals('release')
&& variant.getFlavors().get(0).name.equals('development')) {
variant.setIgnore(true);
}
}
Now, If we try to execute task i.e. ./gradlew DevelopmentRelease. Gradle would stop generating the build and throw exception and would say: This task assembleDevelopmentRelease is not found in the root project.
Use Ant to build at least the production versions. This way you can set certain config values/flags during building. Let's say you have a config.xml file that contains the URL to the server. You can have different Ant build targets that will change the URL to point to the appropriate server. Check out this tutorial. It explains exactly how that is done.
This I think is considered as the bast practice in case you use android studio with gradle.
You may want to look at this article: http://tulipemoutarde.be/2013/10/06/gradle-build-variants-for-your-android-project.html
Also available in youtube video: https://www.youtube.com/watch?v=7JDEK4wkN5I
This also allows you to have two different package name for the same app.
It uses gradle flavors to achieve exactly what you are looking for and is very easy to implement.
You can try gradle buildType and productFlavor. It will allow you to specifiy different Environment variables like url, versionName, etc And applicationId which will allow you to have dev and prod builds.
For more details http://developer.android.com/tools/building/configuring-gradle.html
I don't know what's the best practice in that case, but I do like this:
You could make your app a LIB and create 2 apps: production app and testing app. Import your lib for those apps and build their manifests (it's almost copy paste of the old one). Then you replace your /res/ files that are different in each app... (you could create a config.xml file that have the URL).

Categories

Resources