Adding conditional statements in Android Manifest - android

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.

Related

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

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.

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

ApplicationId conditioned by build type and flavour dimension

I have an application which has 2 flavour dimensions. First dimension, lets call it "brand" has two types:
"strawberry", "cyan"
Both have different applicationIds, but we can focus only on one of those. Lets say "cyan" has applicationId "com.cyan.app".
The second flavour dimension is called say "environment". It has two values:
"prod", "test".
I also have two build types:
"debug", "release"
Now what I'm interested in, is how can I go about configuring the gradle script such that whenever I'm building debug versions there will be applicationIdSuffix which will contain both "debug" string and environment name string. So in the above example it would be:
com.cyan.app.debug.prod and com.cyan.app.debug.test
But if I build release version of an app I want to leave the main applicationId, so com.cyan.app, no matter the environment flavour.
Is there any way I can achieve that with gradle and android gradle plugin?
Ok, I've sit down during the weekend and was able to achieve that (and more). Sharing here so that others can benefit who would like to achieve the same thing as I posted in the question.
So first of all we have to attach ourselves to the applicationVariants.all task like so:
applicationVariants.all { variant ->
//We tweak the package name and application name depending on the variants (so that we can have multiple applications installed on the phone, depending on the
tweakPackageName(variant);
replaceInManifest(variant, 'android:label=\"#string/app_name\"', 'android:label=\"#string/
}
I've separated the logic to other methods so that the code is not clogged. The tweakPackageName method looks pretty simple (as it turned out):
/**
* Method that alters the package name in order to have many different variants of application installed simultanously on a device.
*/
ext.tweakPackageName = { variant ->
def envFlavor = getFlavorOfDimension(variant, flavorDimEnvironmentName);
variant.mergedFlavor.applicationId = variant.mergedFlavor.applicationId + ".${envFlavor.name.toLowerCase()}";
}
getFlavorOfDimension is a simple method to get the flavor of particular dimension I'm interested in (not a biggie so I won't spam with this code here). When we get the flavor we add it to the package name of the mergedFlavor object and we're done.
I also managed to not only change the package name but also to dynamically change the application launcher name, but there are plenty of solutions of that on StackOverflow so I won't be redundant here. All in all the solution to alter the package name works. Hopefully it will help someone like it helped me.

Android configuration in meta-data or in properties file?

I'm writing a library that is used by developers and I have a configuration items like apiKey, environment and others. I want to developer to set these values, my first thought was using Java Properties file that I load in the Application class, but I've seen Google Play and Google Map SDK ask the developers to add their apiKey in meta-data tag in Android Manifest. What is the recommended way?
The recommended android way to let the user define values for your library
from the application module is through meta-tags
it is as easy as using this code on the android-manifest
<meta-data android:name="com.yourproject.apikey" android:value="apikey"/>
and you can get these values from your library like this
try {
ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(activity.getPackageName(), PackageManager.GET_META_DATA);
Bundle bundle = applicationInfo.metaData;
String apiKey = bundle.getString("com.yourproject.apikey");
} catch (NameNotFoundException e) {
//Resolve error for not existing meta-tag, inform the developer about adding his api key
}
The main benefits of this approach are
1) That you can let the user define different values per device type e.g. phone, tablet or sdk-version
(this may not be applicable for your case but it's a good point for sure)
2) You can add complex data as the value in your meta-tag,
and you can use this if you want to add a string-array with all the value that you want to declare.
3) Since meta-tags are basically a bundle it is easier for you to
Read these values on your library code.
Have default values if the user has not declared the required
meta-tags.
(I believe that parsing properties from assets requires more code without any great advantage)
4) You can also add configuration meta-tags for activities,providers and broadcastreceiver on your AndroidManifest.
5) Also I suppose that it is easier to request for the consumers of your library to add a few meta info on his Android Manifest, than adding a properties file on their assets folder.And i believe that this is the main reason Many known libraries use this approach like google-play-service, google-maps, crash reporting libraries and many libraries that provide user analytic.
Good Luck
It´s seems that using meta-data tag is the Android way to achieve this, although I can´t find any official documentation to argue why.
Using meta-data you can define you property value using a resource instead of a static value, this way you can get different values for the same property based on the resources qualifier. This is very useful if your property needs internalization.
Ex:
<meta-data android:value="foo.bar" android:name="#string/hello" />
is easy using this
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API_KEY"/>

How to tell Application's AndroidManifest.xml take #string from a specific package

In my application's AndroidManifest.xml I am trying to tell it to use a string from a /res/values in a particular package instead of the first one one found in one of the libraries (the string my_label could be defined in more than one libraries) :
<application android:icon="#drawable/icon"
android:label="com.example.projname.appmodule:#string/my_label">
The above notation/syntax doesn't work, however. It only accepts the following form which doesn't allow pin-pointing a specific resource file:
<application android:icon="#drawable/icon"
android:label="#string/my_label">
Is it possible at all to tell Application's AndroidManifest.xml take a #string value from a specific /res/values?
EDIT: The interesting thing is, the build environment doesn't complain at all about the "com.example.projname.appmodule:#string/my_label" syntax. But when the application runs, it displays com.example.projname.appmodule:#string/my_label instead of the string value.
As #Nikola Despotoski suggested below, a workaround could be setting this programmatically in the application, but since the build environment doesn't complain about this syntax, I am still wondering whether this a bug in the build env or something that I have been missing.
My workaround, for now, is to make sure that my_label is unique, e.g. my_3F2504E0-4F89-11D3-9A0C-0305E82C3301_label so it can't appear in any other package.

Categories

Resources