I want to setup a variable SERVER_URL and it'll be switched between environments production, test, development.
What I want to do:
#Override
protected void onCreate(Bundle bundle) {
R.urls.SERVER_URL; // is it a valid approach using resources?
}
Is there a way to switch environments(dev, prod, test) without change the code?
What's the best approach to implement this behavior?
Is there a way to configure it in the playstore my variable(SERVER_URL) or must I implement only in code?
There are 2 ways you can do it:
1/ By string resource like you want
Add a resource file called secret_keys.xml or whatever name to separate it from other resources file. Put your keys, api endpoints here as the normal string resource, remember to add translatable="false" if you don't want to mess with localization.
Place that file in app/debug/res/values/. Create a new debug folder if it doesn't exist. Do the same for staging or release, Android will automatically use the folder with the same name as the build type.
2/ By properties files
Create 3 .properties files and put your keys inside:
HOST="http://api.blablabla.com"
CLIENT_ID="hahaha"
CLIENT_SECRET="hehehe"
Bind it to BuildConfig variable in your app build.gradle, do the same for other build types:
def getPropertiesFile = { path ->
Properties properties = new Properties()
properties.load(new FileInputStream(file(path)))
return properties
}
android {
...
buildTypes {
debug {
...
getPropertiesFile('./config/development.properties').each { p ->
buildConfigField 'String', p.key, p.value
}
}
...
}
}
In your app just call BuildConfig.HOST to get the string you want
UPDATE
Ignore these config files in .gitignore:
app/config
secret_keys.xml
You can use different approaches. Ideally you shouldn't change URL at Runtime to minimize the attack surface. This approach could have direct impact on your app's security.
If your target is to modify this URL without touching code, you can do can bind this value at Compile time. You can create application.properties file and modify this file for different target builds (dev,production,test). In your code, you can read the values from properties file instead of hardcoded value. You can place this file in your assets folder and apply necessary obfuscation. This way only the properties file will change and your app's security remains intact.
Another way would be provide this parameter at build time (when you execute gradlew command). You can add commandline parameters which would be added to BuildConfig. In your code, you can simply refer to URL by calling BuildConfig.SERVER_URL. You can follow this SO to achieve this.
In either case I would recommend you to bind this value at compile time.
Related
I have a .property file which at the moment is like this:
com.myapp.token = myGenericString
What I would like to have is to have different values for different build variants. Something like:
if (BuildConfig == Release) {
com.myapp.token = myReleaseString
} else {
com.myapp.token = myOtherString
}
I'm new to Android so I'm not even sure it's possible.
Any suggestions? Thanks!
.properties do not support that, but you can do this with string resources, for example:
app/src/main/res/values/strings.xml and app/src/debug/res/values/strings.xml
one does not even have to differ in code then, the resource location is internally being changed.
I have two options how use the link for the application when I need to open the link in just simple browser.
Rather I save it in the strings.xml file as
<string name="link_openBrowser_calendar" translatable="false">/schedule/</string>
that looks quite ugly lets say or just to save it in the Constants.java file as
final static final String linkSchedule = "/schedule/";
In some cases I have a lot of such links, that require additional parameter
<string name="link_openBrowser_account_byId" translatable="false">
/accounts/account/?id=%1$s
</string>
or even more parameters
<string name="view_meeting_time_inOneDay_period">%1$s at \n%2$s – %3$s</string>
that is useful when part of the string need to be translated, but in the code it looks quite massive and hardly to read.
String time = mEvent.isAllDay() ? getString(R.string.view_meeting_time_inOneDay_allDay, isTodayStr)
: getString(R.string.view_meeting_time_inOneDay_period, isTodayStr, startStr, finishStr);
Which one of these methods is more productive in the ways of scalability, performance or just clearness? I believe at some point it matters where to store this links, while they are not used all the time and only need "on the go", rather I spend memory on storing them as static.
If you need text strings for your application with optional text styling and formatting, strings.xml is convenient. For instance, you can use it to translate your strings into other languages. Android takes a look at user preferences and selects the strings.xml file from the correct localization folder (values is the base one, values-xx stands for other languages). On the other hand, you should use a separate class (e.g., Constants.java) for other strings which you need to use in the global scope. Defining that kind of constant strings in Constants.java is the best practice.
I have found that it is much better to store the resource strings that represent links to API or server links in the .gradle file. The idea here is that during the development stage we want to test our API or whatever with debug/dev API links, so with setting it in the .gradle file we can create different strings for each build type.
android {
...
buildTypes {
release {
buildConfigField("String", "API_URL", "\"https://api.release\"")
// These values are defined only for the release build, which
// is typically used for full builds and continuous builds.
buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
resValue("string", "build_time", "${minutesSinceEpoch}")
...
}
debug {
buildConfigField("String", "API_URL", "\"https://api.debug\"")
// Use static values for incremental builds to ensure that
// resource files and BuildConfig aren't rebuilt with each run.
// If they were dynamic, they would prevent certain benefits of
// Instant Run as well as Gradle UP-TO-DATE checks.
buildConfigField("String", "BUILD_TIME", "\"0\"")
resValue("string", "build_time", "0")
}
}
}
...
I have some value in string.xml, But I took app name value from server using json and wants to be a app name. Or set to a string.xml.
You can go for SharedPreference or database storage for taken values of string which u want to give it for app name. Not sure about this but try once.
The Class com.google.api.client.googleapis.services.AbstractGoogleClient has a function
public Builder setApplicationName(String applicationName) {
this.applicationName = applicationName;
return this;
}
When using Gradle to generate your client libraries using appengineEndpointsInstallClientLibs You should be able to create an endpointbuilder for your endpoints:
private YourEndpoint.Builder endpointBuilder = new YourEndpoint.Builder(
AndroidHttp.newCompatibleTransport(), new JacksonFactory(),
new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
}
}
);
then... to get rid of the warning:
endpointBuilder.setApplicationName("Application Name");
If you want it to display on the home screens, then I don't think there is really any way to do this as it reads out of the AndroidManifest.xml file.
Saying that, if you have a defined set of values, then consider using the Gradle file to create the string depending on which flavor/build type you're using.
This can be done fairly simply with:
resValue "string", "app_name", VARIABLE_NAME
Put the values in gradle.properties, these are one line per value, e.g.
VARIABLE_NAME="My App Name"
Using productFlavors and buildTypes will give you more control, but not allow you to change the values from a server based file.
I hope this helps.
I have a value for a String in String.xml
<string name="id">4</string>
and I have a class which contains a variable
public static int Id=1;
Now what I need is I want to get either of these two values in the gradle, which will check a condition and based on the condition it will rename my app. Below given is the part of the gradle code
def identifier //here i need to get value from the java or xml
switch(identifier)
{
case 1:
temp="ApplicationNewName";break;
}
newName=newName.replace("-release",temp);
output.output.File=new File(output.outputFile.parent,newName);
My question is that, Can i access the variables initialised in the java file or string xml in gradle file ?
You're approaching this problem backwards, Gradle gives you the ability to set those variables within the script itself and then you can further access those variables throughout your Android code. Here's a relevant answer for how you can set build configuration variables: https://stackoverflow.com/a/17201265/2168085
It also sounds like you are trying to build different apps from a single code base, or build variations of those apps. If that's the case then you should really look into build flavors to solve this problem. Essentially a build flavor allows you build different apps from a single main code base and apply variations or new functionality to the different flavors. This can be as basic as having a free and paid version of an app or a full white label code base where you can build very different apps from the same master code base. In Android these are more commonly known as build variants and the developer documentation gives plenty of good information on how to get started: https://developer.android.com/studio/build/build-variants.html
My Problem might not be valid. And the points I mention here might be little incorrect as I am neither perfect nor expert.
I have a shopping application and I want to start building a modular application. Like I want to add Affiliate User( the module which adds certain functionality or this will display some extra pages in an application) in the application.
A similar situation happens in the Frameworks: We add and enable the certain module and in return framework load everything as required.
for this, I want following changes like:
add an entry in the NavigationView displaying "Affiliate Label".
load fragments (just adding one more fragment for one more label/option from navigation view).
Let's say I have a library project that contains a Fragment and all relevant code.
How can I build the application automatically let's say just by writing "true" somewhere in the XML?
Automatically here means label is added, Intents are performed on click of label etc.
<Modules>
<enable>true/false</enable>
</Modules>
This is just the simple scenario.
You could do this (like everywhere when it comes to writing code) in many possible ways.
The "file" way:
Make a new file named something like modules.txt with key value pairs. Load the file and check whether a module is enabled or not.
The "Constant" way:
Make an abstract class which only contains public static final variables which describe your modules.
The "package manager" way:
See create Android Application plugins/extensions (apk)
The "multiple" apk way:
Note that this is not reccomended!
we encourage you to develop and publish a single APK
multiple apk support
To add to codewing's answer, you can also use Gradle's resource management capabilities to accomplish this, so you only ever need to look in one place for an enabled/disabled status.
For this, you have 2 solid options.
The first starts with a boolean which can be split by flavor:
<bool name="module_x_enabled">true</bool>
The second would be to inject your values into a String resource after Gradle merges the resources by adding something like this to your build.gradle file, then comparing that enabled value:
<string name="module_x_enabled">MODULE_X_ENABLED_PLACEHOLDER</string>
android.applicationVariants.all{ variant ->
variant.mergeResources.doLast{
replaceInValues(variant, 'MODULE_X_ENABLED_PLACEHOLDER', MODULE_X_ENABLED)
}
}
def replaceInValues(variant, fromString, toString) {
File valuesFile = file("${buildDir}/intermediates/res/merged/${variant.dirName}/values/values.xml")
String content = valuesFile.getText('UTF-8')
content = content.replaceAll(fromString, toString)
valuesFile.write(content, 'UTF-8')
}
Where MODULE_X_ENABLED would be a setting in your gradle.properties file like:
MODULE_X_ENABLED=true
Edit: or better yet,
Why not pull the settings from some kind of server so that you don't need to rebuild and relaunch to update a client's module?