How to pass customizable property when Android app launches - android

I would like to launch my Android app in such a way that I can set some external variable that my app can read. It would be nice if this was possible either in Gradle or as part of the debug/run configuration.
In essence, I would like to test for a variable to see if it is set. In this example I would like to set USE_FAKE_DATA:
if (USE_FAKE_DATA) {
...
} else {
...
}
One way is to use build variants and I have done this before. But I'm wondering if another way has been made available.

Gradle File
android {
buildTypes {
debug {
buildConfigField "boolean", "USE_FAKE_DATA", "true"
}
release {
buildConfigField "boolean", "USE_FAKE_DATA", "false"
}
}
}
Java File
class Test extends Activity {
#Override
public void onCreate(Bundle data) {
if (BuildConfig.USE_FAKE_DATA) {
...
} else {
...
}
}
}
Please refer this answer for more.

Related

How to call buildConfigField() from another function in Gradle?

Here's an extremely simplified version of what I'm trying to do in Gradle for my Android app:
android {
buildTypes {
debug {
buildConfigFieldMyWay("keyName", "keyValue")
buildConfigField("String", "keyName", "keyValue") }
}
}
}
def buildConfigFieldMyWay(String keyName, String keyValue) {
buildConfigField("String", keyName, keyValue)
}
The real version is more complex, which is why it would be nice to organize what I'm doing into separate functions.
The problem is that when I do a Gradle sync, I get this error:
No signature of method: build_byqgds5lao5ipgp4gk5ftyud4.android() is applicable for argument types: (build_byqgds5lao5ipgp4gk5ftyud4$_run_closure2) values: [build_byqgds5lao5ipgp4gk5ftyud4$_run_closure2#536e468e]
I think this has to do with the DSL Gradle uses with Android; that when buildConfigField() is called inside of android { buildTypes { debug, that it is running buildConfigField() on a BuildType object. If I call it from a separate function, there is no BuildType for it to operate on.
So I either need to modify the DSL so that my function (buildConfigFieldMyWay()) can be called in android { buildTypes { debug and operate on the BuildType object, or I need to pass the BuildType to the function. There's likely some simple syntax I need to use here that I'm not finding. What is it?
You can pass the build type inside the function buildConfigFieldMyWay:
def buildConfigFieldMyWay(buildType, String keyName, String keyValue) {
buildType.buildConfigField("String", keyName, keyValue)
}
And then, you can get the build type using it:
android {
buildTypes {
debug {
buildConfigFieldMyWay(it, "keyName", "keyValue")
buildConfigField("String", "keyName", "keyValue")
}
}
}

How to unit test the app behavior for different Gradle buildTypes in Android?

I am trying to test the behavior of different stages in an Android app. Considering the official docs, I use different Gradle BuildTypes in the module's build.gradle to define the settings needed for each stage.
Each buildType block contains a String with a similar key STAGE_NAME and a different value for each stage for example test_stage, and uat_stage.
The value of those is being accessed in the app using BuildConfig.STAGE_NAME to change some of the parameters like API endpoints accordingly.
But the problem is, there seems to be no official way to unit test the resulting behavior for each stage.
The Android docs, mention a way to change the test buildType which explicitly defines which buildType is being used for testing, but I was wondering if there is another approach to test the resulting behaviors.
The module's build.gradle file:
android {
buildTypes {
release {
...
}
debug {
applicationIdSuffix ".debug"
buildConfigField "String", "STAGE_NAME", "\"debugStage\""
...
}
testStage {
initWith(debug)
buildConfigField "String", "STAGE_NAME", "\"testStage\""
...
}
uatStage {
initWith(debug)
buildConfigField "String", "STAGE_NAME", "\"uatStage\""
...
}
}
}
Example Retrofit interface which is supposed to use different API URLs for each stage by accessing the STAGE_NAME and find out which of the URLs should be used as API_URL:
interface FakeService {
#GET("stuff")
suspend fun getStuff(): List<Stuff>?
...
companion object {
// Different urls which should be used for each stage:
private const val URL_DEBUG = "https://debug_api.example.com/"
private const val URL_TEST = "https://test_api.example.com/
private const val URL_UAT = "https://uat_api.example.com/"
private const val URL_PROD = "https://prod_api.example.com/"
// This is where we access the STAGE_NAME to figure out which stage url should be used
val API_URL_TO_USE = getUrl(BuildConfig.STAGE_NAME)
fun getUrl(stageName: String?): String {
return when (stageName) {
"debugStage" -> URL_DEBUG
"testStage" -> URL_TEST
"uatStage" -> URL_QA
else -> URL_PROD
}
}
}
}
For example in this retrofit interface, I would like to test the behavior of getUrl function to make sure it returns the right URL for each stage.
UPDATE:
After looking at this question, I was wondering maybe one way of testing this, could be mocking the BuildConfig. But since the STAGE_NAME is only available after a successful build, it can not be mocked normally.

Handling minor differences in build variants

I have an application with 2 product flavors and as a result 4 build variants:
1) freeDebug
2) freeRelease
3) paidDebug
4) paidRelease
I have a Fragment that contains an ad, and it takes 2 lines to implement:
#Nullable #BindView (R.id.bottomsheet_ad) AdView mAdView;
mAdView.loadAd(adRequest);
So because of 2 lines, I have to potentially maintain 2 or more files.
I'm considering 2 solutions:
1) Check flavor at runtime:
if (BuildConfig.FLAVOR.equals("flavor123")) {
...
}
2) Create a common flavor and point needed variants in gradle, as explained here:
android {
...
productFlavors {
flavorOne {
...
}
flavorTwo {
...
}
flavorThree {
...
}
flavorFour {
...
}
}
sourceSets {
flavorOne.java.srcDir 'src/common/java'
flavorTwo.java.srcDir 'src/common/java'
flavorThree.java.srcDir 'src/common/java'
}
}
Which solution would be better? And is checking flavor at runtime as above considered polluting the code?
You can add something like following to appropriate flavor in your build.gradle
buildConfigField "boolean", "showAds", 'true'
And then use like following (your main src files will still be used for additional flavors you add):
if (BuildConfig.showAds) {
}

Avoid code duplication - How to create functions (and call them) in gradle?

I've been looking for a few minutes over the internet on how to create functions and call them inside build.gradle without success. Since I found nothing I'm not sure if I'm searching for the right concept-keywords or if that's even possible.
I have two buildTypes:
release {
}
debug {
}
And I woud like to call this snippet() below inside both of them without duplicating it, or in other words, to create a function:
def propsFile = rootProject.file('properties')
def M_PROP = "mProp"
if (propsFile.exists()) {
//Math
}
Generating something like:
buildTypes {
release {
snippet()
}
}
debug {
snippet()
}
}
is that possible and how am I able to do this?
Perhaps you want
buildTypes {
[release, debug].each { buildType ->
if (foo) {
buildType.doStuff()
}
}
}
Or maybe
ext.snippet = { buildType ->
if (foo) {
buildType.doStuff()
}
}
buildTypes {
snippet(release)
snippet(debug)
}
Note: There's also the with { ... } method in groovy so
buildType.doStuff1()
buildType.doStuff2()
buildType.doStuff3()
can be written as
buildType.with {
doStuff1()
doStuff2()
doStuff3()
}

Dagger 2 Android Subcomponents override

if I create a subcomponent that I want to use in a specific feature with dagger lets say:
#TransactionsActivityScope
#Subcomponent(modules = {TransactionsModule.class})
public interface TransactionsComponent {
TransactionsManager provideTransactionsManager();
void inject(TransactionsFragment transactionsFragment);
void inject(TransactionsFilterActivity transactionsFilterActivity);
}
I add it in the main app component with a plus:
TransactionComponent plusTransactionSubcomponent(TransactionModule transactionModule);
and use it in the fragment:
public class TransactionsFragment {
..
..
..
#Override
protected void setupGraph(DaggerAppGraph graph) {
graph.plusTransactionsSubcomponent(new TransactionModule()).inject(this);
}
}
What is the correct way to override this subcomponent in Espresso tests.
For components and component dependencies it is straight forward where you just write a TestAppComponent that extends the "original" component and punch the MockModules in it, but how to do this cleanly with Subcomponents?
I also took a look at the Dagger AndroidInjector.inject(this); solution for components and activity components would be similar but I see no way to do it cleanly for subcomponents and fragments.
I believe it would be suboptimal to write methods and overrides the Activity/Fragments component setters and do the overrides there.
Am I missing something?
This was easy on the original Dagger, but not using Dagger 2. However, here is the solution: create a mocked flavor and a mocked module with exactly the same classname, filename and location. Now run your ui tests using the mocked flavor.
You can see in my test project how it is done.
I use the real module in my app. Located at src/prod/.../ContentRepositoryModule.java
I use a mocked module when testing: Located at src/mock/.../ContentRepositoryModule.java
My mocked module then references the FakeContentRepository, just as you were planning to do.
In the build.gradle:
flavorDimensions "api", "mode"
productFlavors {
dev21 {
// min 21 has faster build times, also with instant build
minSdkVersion 21
dimension "api"
}
dev16 {
minSdkVersion 16
dimension "api"
}
mock {
dimension "mode"
}
prod {
minSdkVersion 16
dimension "mode"
}
}
// remove mockRelease:
android.variantFilter { variant ->
if (variant.buildType.name == 'release'
&& variant.getFlavors().get(1).name == 'mock') {
variant.setIgnore(true);
}
}
So again: this test project shows it all.
In our app we use additional wrapper to manage subcomponents scope with name ComponentStorage. Our Application create this object, TestApplication overrides it and return TestComponentStorage. So we can easily override method plusTransactionSubcomponent and return component with mocked module.
public class ComponentStorage {
protected TransactionComponent transactionComponent;
protected AppGraph graph;
public ComponentStorage() {
graph = buildGraph();
}
public TransactionComponent plusTransactionSubcomponent(TransactionModule transactionModule) {
if(transactionComponent == null) {
transactionComponent = graph.plusTransactionsSubcomponent(new TransactionModule());
}
return transactionComponent;
}
public AppGraph buildGraph() {
return DaggerAppGraph.create();
}
// to manage scope manually
public void clearTransactionSubcomponent() {
transactionComponent = null;
}
}
public class TestComponentStorage extends ComponentStorage{
#Override
public TransactionComponent plusTransactionSubcomponent(TransactionModule transactionModule) {
if(transactionComponent == null) {
// mocked module
transactionComponent = graph.plusTransactionsSubcomponent(new TestTransactionModule());
}
return transactionComponent;
}
}
In client code you will use it componentStorage.plusTransactionsSubcomponent(new TransactionModule()).inject(this)
If you need full code, leave comment I will create gist for this.

Categories

Resources