BuildConfigField R.style.AKTheme is not accessible in androidTest BuildConfig.java class - android

My project has different build types i.e. debug, beta and production and also have different product flavors i.e. QA and Integration. I have defined
a
buildConfigField 'int', 'APP_THEME', 'R.style.AKTheme'
in the productFlavors to have a separate theme for each flavor. The generated BuildConfig.java for app source set have the APP_THEME field and it is working as expected.
Recently I have started writing instrumentation tests for my app. When I try to run these tests Android studio gives me the error that can not resolve AKTheme i.e.
final int APP_THEME = R.style.AKTheme in the generated BuildConfig.java for the test source set.
It seems that R.style.AKTheme is not accessible to the generated BuildConfig.java file (test source set). I searched over internet but didn't find any help.

R.style.AKTheme is a reference, not a value, while in BuildConfig you can only use values.
There are couple of ways to achieve what you want:
Use the String name of the style in BuildConfig:
buildConfigField 'String', 'APP_THEME', '"AKTheme"'
and then in code to get the style res id:
int style = context.getResources().getIdentifier(BuildConfig.APP_THEME, "style", context.getPackageName());
Now you can use style.
You can use different source-sets.
If you are using different buildtypes, you can create a directory for that build type, and put any different resources specially for that build type in that directory. The directory should be created in the same directory as main sources directory, and named exactly the same as the buildType. Details: https://developer.android.com/studio/build/build-variants

I had quite the same issue. I had this in my build.gradle to get the right ssl certificate depending on the env. :
buildConfigField 'int', 'SSL_CERTIFICAT_RAWRES_', 'R.raw.devcert'
This was working to build and run the project, but I faced an issue when I wanted to run the task : "compileDebugAndroidTestJavaWithJavac" (used for sonarqube in my case). The compiler didn't found the resource file R when he automatically generates the buildConfig file.
The solution was to put a String to identify my certificate file "devcert" in the build.gradle instead of using directly the resInt "R.raw" :
buildConfigField 'String', 'SSL_CERTIFICAT_RAWRES_STRING', '"devcert"'
and then in my code, I get the raw file certificate like this :
final int raw = context.getResources().getIdentifier(BuildConfig.SSL_CERTIFICAT_RAWRES_STRING, "raw", context.getPackageName());
That way, the generated buildConfig found correctly the String identifier to retrieve the wanted raw file using the code above.

I found my answer here: https://developer.android.com/studio/test#create_instrumented_test_for_a_build_variant
For my case my test project and my main project is referencing a different package name, e.g. at the top of BuildConfig.java one referencing to package 'com.xxx.xxx' while one is referencing to 'com.xxx.xxx.test'
adding the line
testApplicationId = "com.xxx.xxx"
in defaultConfig in app level build.gradle file solves the issue for me

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)
}

Redeclaration error with same classname in different Source Sets

Similar to flavor variants, I was expecting the ability to have source set variants for the same classname. For example,
androidTest directory
class RobotTest(val name: String = "androidTest")
main directory
class RobotTest(val name: String = "main")
test directory
class RobotTest(val name: String = "test")
Android Studio is okay when I have the same classname in just 2 of the sources:
main and test
main and androidTest
However, once I add the same class to all three, Android Studio shows an Analysis error in test and androidTest with the message:
Redeclaration: RobotTest
I ran the code in a unit test, instrumented test, and in the app and everything compiled fine (no build errors) and when logging RobotTest().name, I correctly get the 3 different values for each class context.
Does anyone have any idea how to fix this or if this isn't allowed?
When you have created a product flavor you should not place your varianted class into main source set.
Keep only common for all variants files into main
For example, you defined:
flavorDimensions("api")
productFlavors {
create("real") {
dimension = "api"
}
create("mock") {
dimension = "api"
}
}
Since that, you have exactly TWO build variants: real OR mock.
So, you have to have exactly TWO versions of your class (if it should be different):
/src/real/kotlin/com/example/SomeApi.kt
/src/main/kotlin/com/example/ (no file here)
/src/mock/kotlin/com/example/SomeApi.kt
UPD:
All above is correct only for sources! If you create 4 flavors, you must copy your Java/Kotlin class into 4 folders, you don't have a 'default' one.
For resources, the logic is different.
Gradle, at first, takes the resource file (some XML file) from the default folder 'main' and then replaces it with the overridden one from the flavor folder if the file exists there. If you create 4 flavors, you can keep a default in 'main' and you can override it only once.

what is the android-studio way of storing configurations- key/value pairs for test/prod?

I want to store parameters such as admob-id, and folder path
these parameters are different for test /prod
how should I approach this in Android studio (there are properties file in the Spring framework, i'm looking for the right way for Android studio IDE )
Thanks
You can either use the buildConfigField or the resValue method of the buildType block in the Android Gradle Plugin. The former generates global Java constants while the latter generates Android resources.
You can review the official docs showing an example of using both here.
I'll elaborate here anyway in case it helps.
First, you define one or more config fields or res values in your build.gradle file. For your example for an AdMob ID, you'd define string constants.
android {
buildTypes {
release {
buildConfigField "String", "ADMOB_ID", '"ConstantIdForRelease"'
resValue "string", "admob_id", "StringResourceForRelease"
}
debug {
buildConfigField "String", "ADMOB_ID", '"ConstantIdForDebug"'
resValue "string", "admob_id", "StringResourceForDebug"
}
}
This would generate BuildConfig.java and temp generated.xml files for both release and debug variants:
Release:
/app/build/generated/source/buildConfig/release/your/package/app/BuildConfig.java:
public final class BuildConfig {
public static final String ADMOB_ID = "ConstantIdForRelease";
}
/app/build/generated/res/resValues/release/values/generated.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Values from build type: releaes -->
<string name="admob_id" translatable="false">StringResourceForRelease</string>
</resource>
Debug:
/app/build/generated/source/buildConfig/debug/your/package/app/BuildConfig.java:
public final class BuildConfig {
public static final String ADMOB_ID = "ConstantIdForDebug";
}
/app/build/generated/res/resValues/debug/values/generated.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Values from build type: debug -->
<string name="admob_id" translatable="false">StringResourceForDebug</string>
</resource>
Then, in your code, you would just use the constant or resource like normal:
callMethodThatNeedsAdModId(BuildConfig.ADMOB_ID);
// OR
callMethodThatNeedsAdModId(context.getString(R.string.admob_id));
The actual value of the build config variable or string resource will resolve based on the current build type / variant.
Either way works, so which should you use?
If you need to access the config string in XML resources (say you want to change the label of an Activity in the AndroidManifest.xml file in debug), you'd use the resource. Otherwise, just use the build config property since you won't need a Context to access it.
Hope that helps!
string resources can be used, in combination with source-sets.
when defining the production values in main and overriding in debug, this should suffice.
for example:
src/main/res/values/strings.xml (main is the default for all build types).
src/debug/res/values/strings.xml (unit tests are also build-type debug).
these files will be merged when building; and it will override the values, where the name of a string resource matches. source-set debug might only have a few files, because it only has the overrides.
with AdMob, the issue merely is having different package-names, per build-type (which is not supported); recently ended up adding the alternate package-name with the .debug suffix, too.

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.

How to define static String values in Android that change for test configurations with Gradle?

Hey I am trying to statically define String values that change according to the configuration I am running. So if I run a test configuration, it uses the test API url, but if I run a regular build, it statically sets the real API URL.
I am using two strings files right now, one in the main folder and one in the androidTest folder in Android Studio. This works well for getting different Strings per configuration, but I'de like to do it statically rather than dealing with Resource fetches.
Is this possible?
I have seen this answer for ANT, but I am not sure how to do it with Gradle.
You can generate gradle constants like this:
build.gradle
android {
buildTypes {
debug {
buildConfigField "String", "FOO", "\"foo\""
}
release {
buildConfigField "String", "FOO", "\"bar\""
}
}
}
And access them in your code through BuildConfig.FOO
Note you may need to clean and/or restart your IDE for the to come in to effect.

Categories

Resources