What's the difference between buildConfigField, resValue and manifestPlaceholders? - android

I understand 'how' they are used, but want to know 'when' they are used.
For a case like using separate KEYs for different environments, we can use all three of the mentioned. So, I was curious to know why there are three things to do the same thing and if they are meant for different usages. Thanks!

Using buildConfigField will generate an actual Java constant in your app's generated BuildConfig class. So for your example, you would then have something like this:
public static class BuildConfig {
public static final String BASE_URL = "xxxxxxxxxx";
}
Using resValue will generate a resource of the type you specify into your app's res (resources) directory. So for a string, you'd be able to reference it via XML with #string/base_url or with getResources().getString(R.string.base_url).
Using manifestPlaceholders allows you to add a substitution in AndroidManifest.xml. So for example, GCM requires you to have a <uses-permission> tag with the name set to X.permission.C2D_MESSAGE, where X is your application ID. So if you have multiple build flavors with different application IDs, you can use a manifestPlaceholders tag and then use it in your AndroidManifest.xml where it will be replaced with the correct value, like:
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
For the record, applicationId is automatically added as a manifest placeholder, so there's no need to define it yourself, but that's just an example.
Basically, it depends on where you need it. If it's needed for an XML resource (a layout, menu, etc.), resValue is what you want. If it's needed to be accessed from Java code directly, buildConfigField will do the trick. For substitutions in the manifest, manifestPlaceholders is what you want.

Related

Best Practice to store link in string

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

What does formString(System.getenv("...")) mean?

In build.gradle, the following is under a product flavor:
buildConfigField 'String', 'API_URL', formString(System.getenv("DEV_API_URL"))
What does formString(System.getenv("DEV_API_URL")) mean?
I am used to seeing the formString as a static value (and I can reference it in code as BuildConfig.API_URL") but am having a hard time figuring out what this code means as well as where "DEV_API_URL" is defined. Guidance and links are appreciated!
1) formString must be a custom function defined somewhere in your build.gradle as there is no such function in Groovy or Java. If you cant figure out where it is, use a text search tool like ag (https://github.com/ggreer/the_silver_searcher)
2) System.getenv is a call that retrieves an environment variable defined on your machine, more here: https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#getenv(java.lang.String).

Same code base, different APKs, differents ressources files and different webservices urls

Lets imagine the following case:
I'm building an application for cars, lets say VW. Now Skoda (belongs to the same group) wants the very same application but with different ressources files, and some checks need to be made into the code like. If VW, call this webservice, else if Skoda, call this other one.
I can't really answer to this question: "Is it the same application?"
Yes from the code point of view but no from the play store point of view...
How can I manage to have the same code base but with two different packages name?
I think I am in the good track reading this http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants but if you have any tricks, advice or help to share I would be glad to hear :)
I know this question has been asked, but I'm looking for a solution with gradle & Android Studio.
Tx
Hello you can use product flavor to define the package name for each new application as #stoke said
productFlavors {
app1 {
applicationId "com.example.package.app1"
buildConfigField "String", "API_URL", "\"https://www.myapi.com/apiv1/\""
}
app2 {
applicationId "com.example.package.app2"
buildConfigField "String", "API_URL","\"https://www.myapi.com/apiv2/\""
}
}
For defining different endpoints or url in your builds you can define build variable to use in your code.
Then you can access them like BuildConfig.API_URL in your java code.
For different resources for diferent flavors you need to define 2 directories that match the new flavors names.
Check this for file sctructure and resources.
The id of the application used by googles's play store is defined in the Build.gradle file :
like
productFlavors {
vw {
applicationId = "com.example.my.pkg.vw"
}
skoda {
applicationId = "com.example.my.pkg.skoda"
}
The ressources folder simply needs to be created under the new flavor's auto generated folder. see http://www.vogella.com/tutorials/AndroidStudioTooling/article.html#androidstudio_creatingresourcefolder for more info

Adding conditional statements in Android Manifest

I need a way to assign different Shared User ID(during installation) to the same Android App(same APK) based on the device model during installation.
I couldn't find any documentation about conditional statements being supported in the manifest file.
Any help would be greatly appreciated.
Though this is a pretty old question, but I stumbled upon a similar problem recently and this might help someone who's still searching for a solution. My case was that I had to assign some key in AndroidManifest.xml at runtime based on a condition.
While I couldn't find a way to define conditions in Manifest, I was able to pass the way to Manifest from build.gradle. If the condition can be checked in build.gradle, then you can pass value to Manifest in the following way
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]
}
...
}
And then access it in Manifest as -
<intent-filter ... >
<data android:scheme="https" android:host="${hostName}" ... />
...
</intent-filter>
Documentation - https://developer.android.com/studio/build/manifest-build-variables
Furthermore, you can generate strings, bool and other values from build.gradle which can then be used app-wide.
buildConfigField "int", "FOO", "42"
resValue "string", "app_name", "My App Name Debug"
The closest you can get to conditionality in the manifest is to not include a value as a constant, but rather use a redirect to the resources. You then provide different resources for different languages, devices etc.
The documentation for the manifest element states that the Shared User id should be a string, but for example the Shared User label must be a string resource (since you would typically need to translate it). Whether you can get away with making the id a resource as well is hard to guess - the documentation is often inaccurate about this, but be aware that what works on one version of Android may not be true for all versions.

Is there a way to use simply "android:" in attribute names with ElementTree?

I'm parsing AndroidManifest.xml with Python's ElementTree. I needed to register the android namespace as http://schemas.android.com/apk/res/android or ElementTree would replace it by something like ns0. It was unintuitive but now it works.
When accessing attributes of a node, I've expected to be able to specify simply eg. elem.attrib["android:versionCode"]. But it didn't work as ElementTree wants me to use it like this:
ET.register_namespace("android", "http://schemas.android.com/apk/res/android")
tree = ET.ElementTree()
tree.parse("AndroidManifest.xml")
root = tree.getroot()
root.attrib["{http://schemas.android.com/apk/res/android}versionCode"] = "3"
even if in the file it was and will be android:versionCode.
As this is counter-intuitive, is there any way to use root.attrib["android:versionCode"] instead?
No, there doesn't appear any other way to specify a 'qualified name' in ElementTree.
from http://effbot.org/zone/element-namespaces.htm
In an Element tree, qualified names are stored as universal names in Clark’s notation, which combines the URI and the local part into a single string, given as > “{uri}local”.
"{http://www.w3.org/1999/xhtml}a"
"{http://effbot.org/namespace/letters}a"
Additional References
http://effbot.org/zone/element-qnames.htm

Categories

Resources