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"/>
Related
I am investigating custom annotation processors for Android applications.
I have a use case where I would like to be able to use an annotation processor to read/amend the AndroidManifest.xml and add the following intent section for all Activities mentioned there in?
<intent-filter>
<action android:name="com.my.package.name.my_activity.open"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Is this possible?
It is a very interesting question but I don't think you could achieve such a task with an annotation processor since those work at Kotlin/Java code generation level.
How would you annotate XML #Something and have it still be valid XML?
Take a look at this:
KoltinConf18 - Annotation processing in Kotlin
At 7:18 Zack goes over annotation processing in Java and it basically says:
Happens at compile time
And you cannot modify code, just generate more
So by using barebones annotation processing you can't really modify the already existing AndroidManifest.xml.
An alternative would be writing a Gradle plugin that generates those bits of XML and merges it with the current XML file that already exists within the project.
Something from the top of my head could be:
Create an annotation and mark all activities that you want to introduce that bit of code
On the plugin side; when you are writing the Gradle task; you may use reflection and figure out which classes are annotated by such extension. Or just make the programmer put those activities in a specific directory inside the source folder, which would be way easier
With the fully qualified class names, you may look at the <activity> nodes in the AndroidManifest.xml, filter out the class names that don't match the list of annotated class names
Modify those nodes with the piece of code you would like to inject.
To get started on how to write a Gradle plugin take a look here
A simple example to get you started could be:
Step 1
You create a separate module to write your plugin if it gets too cumbersome but for this simple example I decided to stick it right in the build.gradle.kts. It doesn't need to be a kotlin Gradle file, but I am more proficient in Kotlin than in Groovy :)
As you can see I have created a text testFile.txt in the root of the project.
In code I just navigate to it and read it; print it's content and then modify it.
You could do the very same thing with your AndroidManifes.xml. Then you would need to recursively iterate over the source files from your srcDir looking for all of those activities annotated by your special annotation and store all of the fully qualified class names inside a List<String>. Then do the necessary replacements inside the AndroidManifest
Note that with this basic configuration the Gradle task appears in the Gradle tab inside the others category, to change that is a little bit off of the scope of annotation processing.
Step 2, profit
It works, as you can see the file has been updated and the println statements show the previous content of the file before modifying it
You could have a template AndroidManifest_template.xml then using a gradle task go through the AndroidManifest_template.xml and generate the real AndroidManifest.xml which would be used to build the app.
In other words, AndroidManest.xml would be a transient part of the build and you could use any XML preprocessor you want to take the template and turn it into the real file.
In case you want to add these intents conditionally depending on flavour of your app, you could use gradle flavours and manifest merging to achieve this - read more about flavours at https://developer.android.com/studio/build/build-variants
Also refer to following question for example of using gradle to modify manifest
https://stackoverflow.com/a/22759572/9640177
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?
I have just made my game app into a library so that I can incorporate it into a variety of "wrapper" projects, each with their own manifest file. This is so that I can easily maintain a variety of different versions e.g. free, paid, alternative markets etc.
I also wanted each wrapper to be able to affect the value of various boolean flags like include_adverts or allow_feature_x. I thought that a good way to do this (correct me if this is a dumb idea) would be for each wrapper project to have its own set of strings defined in its own strings.xml. So it could have things like:
<string name="allow_feature_x">true</string>
But now I have a problem scooping these strings from within the library.
I tried this:
boolean allow_feature_x = my_str2bool(getString(R.string.allow_feature_x));
But I get a allow_feature_x cannot be resolve error.
Can this strings.xml thing be made to work? Or was my scheme fundamentally flawed from the start?
This is exactly what I do for many of my projects.
Just make sure your library project has all the default set of strings defined in it, otherwise you won't be able to reference them, since the library does not know about the "parent".
Then override them in your "parent" projects. You only need to override the ones that are different from default, otherwise it will take the value from the library project.
Hope that makes sense.
Edit: By the way you can reference booleans like so:
<bool name="allow_feature_x">true</bool>
and then access getResources().getBoolean(R.bool.allow_feature_x);
I recently migrated from FB for Android 2.0 to 3.0.
Instead of using
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="#string/app_id"/>
in my AndroidManifest.xml, I wanted to programmatically set my application Id to a session.
I know that I can use Session.Builder.setApplicationId() upon the creation of a session object.
However, when I use UiLifecycleHelper.java, it creates a session without using session.Builder. It only uses its constructor (session = new Session(activity)).
It seems like it assumes that I already defined a meta-data for app id in my manifest file.
However, I want UiLifecycleHelper.java to create a session by using Session.Builder and programmatically applying my application Id in its onCreate().
How can I do it? I google it but haven't found any solution yet.
Is it a common practice to use meta data to set FB application Id in the manifest file? I really don't want to reveal my app Id by putting it in an xml file. Please help!
Your Application ID is NOT considered a secret (just like how a person's profile ID is not a secret).
But if you really don't want to use the xml method, you can copy or subclass UiLifecycleHelper and change the appropriate method to use your own way of creating a Session.
If you want to set it in the code, you can change the method getMetadataApplicationId() from Utility.Java and add your code to set the application id to whatever you like.
In android we have files by name String. Developers define the string values that they used for naming objects in this file. This is a very useful way. Because avoid of hard coding string values(you can change them from a single file, less time to change), also useful to creating multi language application and etc. (for more info just google this).
But the question is this: whether iPhone(Monotouch) have a mechanism like this to define strings on them or developers have to define themselves mechanism for this?
In XCode, you'll find File/New File, then on the left, pick "Resource", and you'll find "Strings File".
From code, you'll be referencing the keys in your strings file with NSLocalizedString:
NSLog("%#", NSLocalizedString(#"YOUR-STRING-KEY-OR-DEFAULT-VALUE", #"Comment about what this is"));
For details on what that second param is for, What is the second parameter of NSLocalizedString()?
Put your strings in a single file. Make them global constants. Access them throughout the app. When you change these, the change will be reflected everywhere.
It's not a big deal to have persistent string references throughout your app. It can be done in any decent programming language and platform I suppose.