How to pass BuildConfig values to dependency module? - android

I have some configured values in BuildConfig of App module. I want to pass those values to MyLib's BuildConfig which is dependency of App module. Is it possible?

This is how I shared the App Version Code and Version Name with a library/module BuildConfig :
Define version code and name in project level gradle file :
ext {
appVersionCode = 1
appVersionName = '1.0.1'
}
Now in your app gradle :
defaultConfig {
....
versionCode $rootProject.appVersionCode
versionName $rootProject.appVersionName
And in your library/module gradle :
defaultConfig {
....
def appVersionCode = $rootProject.appVersionCode
def appVersionName = '\"' + $rootProject.appVersionName +'\"'
versionCode appVersionCode
versionName appVersionName
//This part to get version code and name in library/module BuildConfig file
buildConfigField 'int', 'APP_VERSION_CODE', "$appVersionCode"
buildConfigField 'String', 'APP_VERSION_NAME', appVersionName
A simple project explaining how to share gradle buildConfigField variables with multiple modules : https://github.com/Noddy20/Multi-Module-Gradle-Config

The most easy way is to create a third module(library), and add this module to the dependency of your library module and app module.
Then put the shared build config to the shared third module.
app module <------------------ library module
^ ^
| |
| dependency |dependency
------------ third module -------

No, We cant do that. Dependency module can't access the BuildConfig file of App module.
The only alternative solution for your problem is you need add the same properties to your dependency BuildConfig file.

In general, BuildConfig has static members. so I would suggest reflection to transfer your BuildConfig as a list of model which holds Field/value
We would need a model to include field and value for all class members. Lets call it BuildConfigItem (I suggest to put this class in destination Module):
public class BuildConfigItem {
public final Field field;
public final Object object;
public BuildConfigItem(Field field, Object object) {
this.field = field;
this.object = object;
}
}
Now you can get all class members of BuildConfig with this method. Idea is to convert them to portable phase that can be retrieved on other module independently even without knowing what BuildConfig has:
public static ArrayList<BuildConfigItem> getBuildConfigField() {
ArrayList<BuildConfigItem> list = new ArrayList<>();
Field[] declaredFields = BuildConfig.class.getDeclaredFields();
BuildConfig buildConfig=new BuildConfig();
for (Field field : declaredFields) {
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
try {
BuildConfigItem buildConfigItem = new BuildConfigItem(field, field.get(buildConfig));
list.add(buildConfigItem);
} catch (IllegalAccessException e) {
Log.e(TAG, "error during assigning fields: ", e);
}
}
}
return list;
}
Get your list of BuildConfigItem :
ArrayList<BuildConfigItem> buildConfigItemArrayList = getBuildConfigField();
Then pass it to your module. Here is simple way how to iterate that list to get values:
for (BuildConfigItem buildConfigItem : buildConfigItemArrayList) {
Log.d(TAG,buildConfigItem.field.getName() + ":" + buildConfigItem.object);
}
Here is how to list all values and casting common types:
for (BuildConfigItem buildConfigItem : buildConfigItemArrayList) {
if (buildConfigItem.field.getType() == String.class) {
String value = (String) buildConfigItem.object;
Log.d(TAG, "String:" + buildConfigItem.field.getName() + ":" + value);
} else if (buildConfigItem.field.getType() == int.class) {
Integer value = (Integer) buildConfigItem.object;
Log.d(TAG, "integer:" + buildConfigItem.field.getName() + ":" + value);
} else if (buildConfigItem.field.getType() == boolean.class) {
Boolean value = (Boolean) buildConfigItem.object;
Log.d(TAG, "boolean:" + buildConfigItem.field.getName() + ":" + value);
} else {
Log.d(TAG, "Other:" + buildConfigItem.field.getName() + ":" + buildConfigItem.object);
}
}
Thats it 🙂
You would need to adjust this code if you define custom type of field in BuildConfig. i.e. Date or even more complex type.
Also be aware of that the destination module should have all dependencies of BuildConfig types. ( in case you are using your own object in defining field in BuildConfig)
Good luck,'.

The shortest inline solution. The example sets the VERSION_NAME from the app module to a sub-module with name common:
rootProject.findProject("common")?.let { commonProject ->
commonProject.plugins.whenPluginAdded {
val ext = commonProject.extensions.findByType(com.android.build.api.variant.AndroidComponentsExtension::class) ?: return#whenPluginAdded
ext.finalizeDsl {
ext.onVariants {
it.buildConfigFields.put("VERSION_NAME", BuildConfigField("String", "\"$versionName\"", ""))
}
}
}
}

This how I did this since I didn't want to maintain multiple BuildConfigs for different RN Modules.
Note: I am using react-native but this should be relevant to just vanilla Android\Java.
MainApplication.java
I passed the BuildConfig.class to the dependency constructor.
#Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> array =null;
try {
array= Arrays.asList(new MainReactPackage(),
new RNCustomPackageWithBuildConfig(BuildConfig.class));
} catch (IllegalAccessException e) {
Log.e(LOG_TAG,e.getMessage(),e.getCause());
}
return array;
}
RNCustomPackageWithBuildConfig.java (constructor accepts class param)
public class RNCustomPackageWithBuildConfig implements ReactPackage {
public static HashMap<String,Object> MainBuildConfig;
public RNCustomPackageWithBuildConfig(Class mainBuildConfig) throws IllegalAccessException {
MainBuildConfig= (HashMap<String, Object>) ObjectHelpers.getClassProperties(mainBuildConfig);
}
}
getClassProperties helper method
public class ObjectHelpers {
public static Map<String, Object> getClassProperties(final Class inClass) throws IllegalAccessException {
Map<String, Object> returnVal = new HashMap<>();
for (Field field : inClass.getDeclaredFields()) {
field.setAccessible(true); // You might want to set modifier to public first.
Object value = field.get(inClass);
if (value != null) {
returnVal.put(field.getName(),value);
}
}
return returnVal;
}
}
with in my Android RN module
private void handleIntent(Intent intent) throws Exception {
// insert business
String action = intent.getAction();
String type = intent.getType();
Log.d(LOG_TAG, "handleIntent => Type:" + type + " Action:" + action);
if (Intent.ACTION_VIEW.equals(action)) {
//access public static MainBuildConfig field from RNCustomPackageWithBuildConfig
String someBuildConfigField= RNCustomPackageWithBuildConfig .MainBuildConfig.get("SOME_BUILDCONFIG_FIELD").toString();
}

Related

Testing Room as JUnit Test Not AndroidTest

Trying to test room migration using the MigrationTestHelper class and Robolectric. We want it as a JUnit test because our CI environment cannot fire up an emulator. (Please no answers with CI fixes for emulators, CI is not in my control) Only issue I have is that the test fails because it can't find the schemas. My build.gradle has this in it already
android {
sourceSets {
test.assets.srcDirs += files("$projectDir/schemas".toString())
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
test {
java.srcDirs += "$projectDir/src/sharedTest/java"
}
testOptions {
unitTests {
includeAndroidResources = true
}
unitTests.all {
systemProperty 'robolectric.enabledSdks', '21'
}
}
}
dependencies {
// has all the proper dependencies from mockito adn robolectric to kotlin and junit.
}
Here is the test code but again its mostly just the schema can't be found when the database creation is called. Also the json files are there in the schema directory
#RunWith(RobolectricTestRunner::class)
class Migration19To20Test {
private val migration = MyDatabase.MIGRATION_19_20
private val fromVersion = 19
private val toVersion = 20
#get:Rule
val helper: MigrationTestHelper = MigrationTestHelper(
InstrumentationRegistry.getInstrumentation(),
MyDatabase::class.java.canonicalName,
FrameworkSQLiteOpenHelperFactory())
private val testDatabaseName = "migration-test"
#Test
fun insertsFirmwareVersionFullColumn() {
givenADatabase()
val validateDroppedTables = true
val db = helper.runMigrationsAndValidate(
testDatabaseName,
toVersion,
validateDroppedTables,
migration)
db.query("select * from ${DatabaseConstants.Table.People}").use { cursor ->
cursor.moveToFirst()
assertTrue("table should contain the ${DatabaseConstants.Column.People.NAME_FULL} column as it should have been added",
cursor.columnNames.contains(DatabaseConstants.Column.People.NAME_FULL))
}
}
private fun givenADatabase() {
// Test fails here
helper.createDatabase(testDatabaseName, fromVersion)
}
}
I solved it by copying MigrationTestHelper to my test source code and modified loadSchema method to look like this
private SchemaBundle loadSchema(Context context, int version) throws IOException {
// InputStream input = context.getAssets().open(mAssetsFolder + "/" + version + ".json");
InputStream input = new FileInputStream("./schemas/" + mAssetsFolder + "/" + version + ".json");
return SchemaBundle.deserialize(input);
}
The schemas directory is configured in build.gradle
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [
"room.schemaLocation" : "$projectDir/schemas".toString(),
"room.incremental" : "true",
"room.expandProjection": "true"]
}
}
}
}
As you can see the cause is that this class looks for migration in assets directory because Google assumes that you will run those migration on device. In such case you must include them as part of android assets.
With Robolectric it's just reading from a directory and putting that into a stream.
My scenario was not exactly the same - I had an instrumented migration test case - but perhaps the cause is the same. If you have something like the rule below, aapt will strip the schema files and it will do so in debug builds as well as release ones.
buildTypes {
debug {...}
release {
aaptOptions {
ignoreAssetsPattern '!*.json'
}
}
}

How to override the Robolectric runtime dependency repository URL?

We're trying to use the org.robolectric:robolectric:3.0 dependency from our own internal Nexus repository. The issue is that Robolectric tries to load some dependencies at runtime from a public repository (as mentioned here), and ignores any repository overrides in the build.gradle.
Since we don't have access to that public location from our intranet, my tests timeout after trying to load that dependency:
[WARNING] Unable to get resource
'org.robolectric:android-all:jar:5.0.0_r2-robolectric-1' from
repository sonatype (https://oss.sonatype.org/content/groups/public/):
Error transferring file: Operation timed out
The bottom section of the Robolectric configuration documentation recommends adding this to your Gradle configuration to override the URL:
android {
testOptions {
unitTests.all {
systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
systemProperty 'robolectric.dependency.repo.id', 'local'
}
}
}
Unfortunately, I've tested that and I never see that system property being set. I've printed it out from inside my custom Robolectric runner (which extends RobolectricGradleTestRunner) and that system property remains set to null.
System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));
I also tried to do something similar to this comment (but that method doesn't exist to override in RobolectricGradleTestRunner), and I also tried setting the system properties directly in my custom Robolectric runner, and that didn't seem to help.
#Config(constants = BuildConfig.class)
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
private static final String BUILD_OUTPUT = "build/intermediates";
public CustomRobolectricRunner(Class<?> testClass) throws InitializationError {
super(testClass);
System.setProperty("robolectric.dependency.repo.url", "https://nexus.myinternaldomain.com/content");
System.setProperty("robolectric.dependency.repo.id", "internal");
System.out.println("robolectric.dependency.repo.url: " + System.getProperty("robolectric.dependency.repo.url"));
}
The Robolectric source code does seem to confirm that these system properties exist.
While not a fix for using the properties directly, another way to get this to work is by overriding getJarResolver() in a RobolectricTestRunner subclass and pointing it at your artifact host:
public final class MyTestRunner extends RobolectricTestRunner {
public MyTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
#Override protected DependencyResolver getJarResolver() {
return new CustomDependencyResolver();
}
static final class CustomDependencyResolver implements DependencyResolver {
private final Project project = new Project();
#Override public URL[] getLocalArtifactUrls(DependencyJar... dependencies) {
DependenciesTask dependenciesTask = new DependenciesTask();
RemoteRepository repository = new RemoteRepository();
repository.setUrl("https://my-nexus.example.com/content/groups/public");
repository.setId("my-nexus");
dependenciesTask.addConfiguredRemoteRepository(repository);
dependenciesTask.setProject(project);
for (DependencyJar dependencyJar : dependencies) {
Dependency dependency = new Dependency();
dependency.setArtifactId(dependencyJar.getArtifactId());
dependency.setGroupId(dependencyJar.getGroupId());
dependency.setType(dependencyJar.getType());
dependency.setVersion(dependencyJar.getVersion());
if (dependencyJar.getClassifier() != null) {
dependency.setClassifier(dependencyJar.getClassifier());
}
dependenciesTask.addDependency(dependency);
}
dependenciesTask.execute();
#SuppressWarnings("unchecked")
Hashtable<String, String> artifacts = project.getProperties();
URL[] urls = new URL[dependencies.length];
for (int i = 0; i < urls.length; i++) {
try {
urls[i] = Util.url(artifacts.get(key(dependencies[i])));
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return urls;
}
#Override public URL getLocalArtifactUrl(DependencyJar dependency) {
URL[] urls = getLocalArtifactUrls(dependency);
if (urls.length > 0) {
return urls[0];
}
return null;
}
private String key(DependencyJar dependency) {
String key =
dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getType();
if (dependency.getClassifier() != null) {
key += ":" + dependency.getClassifier();
}
return key;
}
}
}
It should be noted that this relies on two internal classes of Robolectric so care should be taken when upgrading versions.
You can set properties mavenRepositoryId and mavenRepositoryUrl of RoboSettings which are used by MavenDependencyResolver.
Example:
public class CustomRobolectricRunner extends RobolectricGradleTestRunner {
static {
RoboSettings.setMavenRepositoryId("my-nexus");
RoboSettings.setMavenRepositoryUrl("https://my-nexus.example.com/content/groups/public");
}
...
}
As per the linked Github issue, one fix is to configure a settings.xml in your ~\.m2 folder:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<mirrors>
<mirror>
<id>jcenter</id>
<name>JCenter Remote</name>
<mirrorOf>*</mirrorOf>
<url>https://www.example.com/artifactory/jcenter-remote/</url>
</mirror>
</mirrors>
</settings>
<mirrorOf>*</mirrorOf> seems necessary to force Maven to redirect all repository requests to the one remote. See here for more details about mirror settings in Maven.
I found that using a remote of Sonatype is not sufficient, you should use a remote of JCenter or Maven Central in order to obtain all of the transitive dependencies.
As of time of this writing, those previous answers are now obsolete. If you refer to the latest robolectric documentation you need to override the robolectric.dependency.repo.url property like so:
android {
testOptions {
unitTests.all {
systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
systemProperty 'robolectric.dependency.repo.id', 'local'
}
}
}

DexFile in 2.0 versions of Android Studio and Gradle

I am using the following code to instantiate all the classes included in a certain package.
DexFile df = new DexFile(getPackageCodePath());
for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {
String className = iter.nextElement();
if (className.contains(packageName) && !className.contains("$")) {
myClasses.add(Class.forName(className).newInstance());
}
}
Unfortunately it is not working properly anymore. Since Android Studio 2 and Gradle 2.0.0, the DexFile entries no longer include all the classes within the app but only the classes belonging to the com.android.tools package.
Is this a known issue?
Looks like this issue is related to the new InstantRun feature in the Android Plugin for Gradle 2.0.0.
getPackageCodePath() gets a String pointing towards the base.apk file in the Android file system. If we unzip that apk we can find one or several .dex files inside its root folder. The entries obtained from the method df.entries() iterates over the .dex files found in that root folder in order to obtain all of its compiled classes.
However, if we are using the new Android Plugin for Gradle, we will only find the .dex related to the android runtime and instant run (packages com.tools.android.fd.runtime, com.tools.android.fd.common and com.tools.android.tools.ir.api). Every other class will be compiled in several .dex files, zipped into a file called instant-run.zip and placed into the root folder of the apk.
That's why the code posted in the question is not able to list all the classes within the app. Still, this will only affect Debug builds since the Release ones don't feature InstantRun.
To access all DexFiles you can do this
internal fun getDexFiles(context: Context): List<DexFile> {
// Here we do some reflection to access the dex files from the class loader. These implementation details vary by platform version,
// so we have to be a little careful, but not a huge deal since this is just for testing. It should work on 21+.
// The source for reference is at:
// https://android.googlesource.com/platform/libcore/+/oreo-release/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
val classLoader = context.classLoader as BaseDexClassLoader
val pathListField = field("dalvik.system.BaseDexClassLoader", "pathList")
val pathList = pathListField.get(classLoader) // Type is DexPathList
val dexElementsField = field("dalvik.system.DexPathList", "dexElements")
#Suppress("UNCHECKED_CAST")
val dexElements = dexElementsField.get(pathList) as Array<Any> // Type is Array<DexPathList.Element>
val dexFileField = field("dalvik.system.DexPathList\$Element", "dexFile")
return dexElements.map {
dexFileField.get(it) as DexFile
}
}
private fun field(className: String, fieldName: String): Field {
val clazz = Class.forName(className)
val field = clazz.getDeclaredField(fieldName)
field.isAccessible = true
return field
}
for get all dex files of an app use below method.
public static ArrayList<DexFile> getMultiDex()
{
BaseDexClassLoader dexLoader = (BaseDexClassLoader) getClassLoader();
Field f = getField("pathList", getClassByAddressName("dalvik.system.BaseDexClassLoader"));
Object pathList = getObjectFromField(f, dexLoader);
Field f2 = getField("dexElements", getClassByAddressName("dalvik.system.DexPathList"));
Object[] list = getObjectFromField(f2, pathList);
Field f3 = getField("dexFile", getClassByAddressName("dalvik.system.DexPathList$Element"));
ArrayList<DexFile> res = new ArrayList<>();
for(int i = 0; i < list.length; i++)
{
DexFile d = getObjectFromField(f3, list[i]);
res.add(d);
}
return res;
}
//------------ other methods
public static ClassLoader getClassLoader()
{
return Thread.currentThread().getContextClassLoader();
}
public static Class<?> getClassByAddressName(String classAddressName)
{
Class mClass = null;
try
{
mClass = Class.forName(classAddressName);
} catch(Exception e)
{
}
return mClass;
}
public static <T extends Object> T getObjectFromField(Field field, Object arg)
{
try
{
field.setAccessible(true);
return (T) field.get(arg);
} catch(Exception e)
{
e.printStackTrace();
return null;
}
}

android gradle build set class attribute value

I want to set attribute values in my Application class from the build.gradle file like for example:
MyApplication.URL = "someurl.com"
that should be determined per build,
I tried :
productFlavors {
myApp {
qualified.package.path.MyApplication.URL = "someurl.com"
}
}
but it failed
You can write your fields inside BuildConfig class and then get them from there.
productFlavors {
myApp {
buildConfigField "String", "URL", "\"someurl.com\""
}
}
public class MyApplication extends Application {
private String URL;
#Override
public void onCreate() {
URL = BuildConfig.URL;
}
}

How to detect whether android app is running UI test with Espresso

I am writing some Espresso tests for Android. I am running in the the following problem:
In order for a certain test case to run properly, I need to disable some features in the app. Therefore, in my app, I need to detect whether I am running Espresso test so that I can disable it. However, I don't want to use BuildConfig.DEBUG to because I don't want those features to be disabled in a debug build. Also, I would like to avoid creating a new buildConfig to avoid too many build variants to be created (we already have a lot of flavors defined).
I was looking for a way to define buildConfigField for test but I couldn't find any reference on Google.
Combining Commonsware comment + Comtaler's solution here's a way to do it for any test class using the Espresso framework.
private static AtomicBoolean isRunningTest;
public static synchronized boolean isRunningTest () {
if (null == isRunningTest) {
boolean istest;
try {
// "android.support.test.espresso.Espresso" if you haven't migrated to androidx yet
Class.forName ("androidx.test.espresso.Espresso");
istest = true;
} catch (ClassNotFoundException e) {
istest = false;
}
isRunningTest = new AtomicBoolean (istest);
}
return isRunningTest.get();
}
Combined with CommonsWare's comment. Here is my solution:
I defined an AtomicBoolean variable and a function to check whether it's running test:
private AtomicBoolean isRunningTest;
public synchronized boolean isRunningTest () {
if (null == isRunningTest) {
boolean istest;
try {
Class.forName ("myApp.package.name.test.class.name");
istest = true;
} catch (ClassNotFoundException e) {
istest = false;
}
isRunningTest = new AtomicBoolean (istest);
}
return isRunningTest.get ();
}
This avoids doing the try-catch check every time you need to check the value and it only runs the check the first time you call this function.
How about a flag in BuildConfig class?
android {
defaultConfig {
// No automatic import :(
buildConfigField "java.util.concurrent.atomic.AtomicBoolean", "IS_TESTING", "new java.util.concurrent.atomic.AtomicBoolean(false)"
}
}
Add this somewhere in your test classes.
static {
BuildConfig.IS_TESTING.set(true);
}
Building on the answers above the following Kotlin code is equivalent:
val isRunningTest : Boolean by lazy {
try {
Class.forName("android.support.test.espresso.Espresso")
true
} catch (e: ClassNotFoundException) {
false
}
}
You can then check the value of the property:
if (isRunningTest) {
// Espresso only code
}
i prefer not to use reflection which is slow on android. Most of us have dagger2 set up for dependency injection. I have a test component set up for testing. Here is a brief way you can get the application mode (testing or normal):
create a enum:
public enum ApplicationMode {
NORMAL,TESTING;
}
and a normal AppModule:
#Module
public class AppModule {
#Provides
public ApplicationMode provideApplicationMode(){
return ApplicationMode.NORMAL;
}
}
create a test runner like me:
public class PomeloTestRunner extends AndroidJUnitRunner {
#Override
public Application newApplication(ClassLoader cl, String className, Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return super.newApplication(cl, MyTestApplication.class.getName(), context);
}
}
dont forget to declare it in gradle like this:
defaultConfig {
testInstrumentationRunner "com.mobile.pomelo.base.PomeloTestRunner"
}
Now create a subclass of the AppModule with override method that looks exactly like this and do not mark it as a module above the class definition :
public class TestAppModule extends AppModule{
public TestAppModule(Application application) {
super(application);
}
#Override
public ApplicationMode provideApplicationMode(){
return ApplicationMode.TESTING; //notice we are testing here
}
}
now in your MyTestApplication class that you declared in custom test runner have the following declared:
public class PomeloTestApplication extends PomeloApplication {
#Singleton
#Component(modules = {AppModule.class})
public interface TestAppComponent extends AppComponent {
}
#Override
protected AppComponent initDagger(Application application) {
return DaggerPomeloTestApplication_TestAppComponent.builder()
.appModule(new TestAppModule(application)) //notice we pass in our Test appModule here that we subclassed which has a ApplicationMode set to testing
.build();
}
}
Now to use it simply inject it in production code wherever like this:
#Inject
ApplicationMode appMode;
so when your running espresso tests it will be testing enum but when in production code it will be normal enum.
ps not necessary but if you need to see how my production dagger builds the graph its like this and declared in application subclass:
protected AppComponent initDagger(Application application) {
return DaggerAppComponent.builder()
.appModule(new AppModule(application))
.build();
}
If you are using JitPack with kotlin. You need to change Espresso's package name .
val isRunningTest : Boolean by lazy {
try {
Class.forName("androidx.test.espresso.Espresso")
true
} catch (e: ClassNotFoundException) {
false
}
}
For checking
if (isRunningTest) {
// Espresso only code
}
I'll create two files like below
src/main/.../Injection.java
src/androidTest/.../Injection.java
And in Injection.java I'll use different implementation, or just a static variable int it.
Since androidTest is the source set, not a part of build type, I think what you want to do is hard.
You can use SharedPreferences for this.
Set debug mode:
boolean isDebug = true;
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt("DEBUG_MODE", isDebug);
editor.commit();
Check if debug mode:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
boolean isDebug = sharedPref.getBoolean("DEBUG_MODE", false);
if(isDebug){
//Activate debug features
}else{
//Disable debug features
}
Here is a way to adapt the accepted solution for a react-native Android App.
// MainActivity.java
// ...
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
// ...
#Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
boolean testingInProgress;
try {
Class.forName ("androidx.test.espresso.Espresso");
testingInProgress = true;
} catch (ClassNotFoundException e) {
testingInProgress = false;
}
initialProperties.putBoolean("testingInProgress", testingInProgress);
return initialProperties;
}
};
}
}
You will then be able to access testingInProgress as a prop given to your top-most component (typically App.js). From there you can use componentDidMount or equivalent to access it and throw it into your Redux store (or whatever you are using) in order to make it accessible to the rest of your app.
We use this to trigger some logic in our app to assist us taking screenshots with fastlane.
I would suggest using a boolean variable initialized to false in another class called, for instance, Settings.java:
private static boolean isRunningAndroidTest = false;
This boolean variable would have following setter and getter also defined in Settings.java:
public static void setIsRunningAndroidTest(boolean isRunningAndroidTest) {
Settings.isRunningAndroidTest = isRunningAndroidTest;
}
public static boolean getIsRunningAndroidTest() {
return isRunningAndroidTest;
}
One could then toggle this isRunningAndroidTest variable to true at the beginning of the androidTest file by calling the setter defined in Settings.java as follows:
Settings.setIsRunningAndroidTest(true);
Finally, the actual value of this boolean variable can later be checked in any other files by calling its corresponding getter defined in Settings.java as follows:
if (Settings.getIsRunningAndroidTest()) {
// Do something in case an androidTest is currently running
} else {
// Do something else in case NO androidTest is currently running
}

Categories

Resources