I am using one service where it's required to setup API key in AndroidManifest like this:
<meta-data
android:name="service_api_key"
android:value="#string/my_api_key" />
The problem with this is that I have a couple of flavors of my app and I need to setup different API keys for each flavor. Each flavor needs to have different API key for debug and release:
flavor1
- debug key: key1
- release key: key2
flavor2
- debug key: key3
- release key: key4
flavor3
- debug key: key5
- release key: key6
What would be the recommended way to accomplish this?
What would be the recommended way to accomplish this?
Step #1: Create sourcesets for each build variant (src/flavor1Debug/, src/flavor1Release/, etc.) for which you need a different API key than whatever you have in src/main/.
Step #2: In each sourceset, have a res/values/strings.xml file that contains that build variant's value for my_api_key.
Step #3: Beer.
not tested but I imagine you could try something like this...
applicationVariants.all {variants ->
variant.productFlavors.all { flavor ->
flavorChoosed = " ${flavor.name}"
}
}
release {
switch(flavorChoosed){
case "flavor1":
resValue "string", "flavorId", apiKeyRealeseFlavor1
break
.....
}
}
debug{
switch(flavorChoosed){
case "flavor1":
resValue "string", "flavorId", apiKeyDebugFlavor1
break
.....
}
}
<meta-data
android:name="service_api_key"
android:value="${flavorId}" />
For each Product Flavor you have added to your gradle file you should add a string.xml resource file for values.
When you switch your build variant, Android studio will be smart enough to grab the matching value for your build.
If you dont specify one, then it defaults to the main one.
EDIT:
Then for the source set, select the release or debug version of your productFlavor:
Related
i am developing an android application using android studio 2.3. I added google map activity to my project using built in add activity dialog.
When i check the google map api key it is saved in a separate xml file inside values folder but under debug varient. I need to add a new api key that is going to use only for relase mode (builds). How do i add such a separate api key xml file for release mode as well?
Thanks in advance!!
AndroidManifest.xml:
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="#string/google_maps_api_key"/>
app/build.gradle:
android {
...
buildTypes {
debug {
resValue "string", "google_maps_api_key", "[YOUR DEBUG KEY]"
}
release {
resValue "string", "google_maps_api_key", "[YOUR RELEASE KEY]"
}
}
}
Just create same xml file in release folder.
You're default debug key is probably in file project_location/src/debug/res/values/some_file_name.xml.
You need to create same file in project_location/src/release/res/values/some_file_name.xml And put you'r release key there.
Recreate the debug and\or release folder structure in the same folder as your "main" one. Any files placed here will replace the ones in main depending on the build type.
I want to release my apk on play store. Initially I will release it on alpha, then beta and if everything goes well I will release it on production. Can we define different api endpoint for each in buildType inside gradle. if yes then how? As I just want to change the end point of API I am calling throughout my application. Like if I release my apk on alpha the api that it points will be http://test.alpha.bla.bla
for beta: http://test.beta.bla.bla
for production: http://test.production.bla.bla.
so in this my all version of app (alpha/beta/production) will be having same version code without any need to upload new apk.
Thanks.
I assume that you mean you want 3 different build targets (and thus 3 different uploads to google play):
in your build.gradle you have the android part, in there you can define productFlavors like the following:
productFlavors{
alpha{
buildConfigField 'string', 'server','http://test.alpha.bla.bla'
}
}
But you could also make enums and refere those (instead of the type "string" you would have to specifiy the full package name + enum type , and in the last part (the value), the full package name + enum )
you can then reference the server by using (in java)
BuildConfig.server; //this would be http://test.alpha.bla.bla
I would put it in strings.xml. Each build variant can have its own copy with a different value.
Let's say we have strings_test.xml, which stores string values for testing and should be shown in a debug-release. When the apk gets build as a release version all values should be change to an empty string e.g. <string name="test_some_card_text">#string/empty</string>.
Is there a possibility to achieve this?
As always, thanks in advance.
Yes you can do that inside your app gradle under buildTypes..
buildTypes {
mybuild {
resValue "string", "test_some_card_text", '"test"'
resValue "string", "other_text", '"other"'
}
debug {
resValue "string", "test_some_card_text", '"test"'
resValue "string", "other_text", '"other"'
}
}
Then access it like this.
getApplicationContext().getResources().getString(R.string.test_some_card_text);
getApplicationContext().getResources().getString(R.string.other_text);
For build you need to select that build variants and have to build it.
Yes, Gradle lets you override strings.
Add this inside buildTypes{} in your app/build.gradle
debug {
applicationIdSuffix "debug"
}
That should create a directory titled debug next to main. If not then manually create one. (Seriously, I haven't tried this, but I know this is possible.)
Then if your strings_test.xml is under res/values, create similar directory structure under debug/ and put your strings_text.xml with debug specific strings there. This will show up in your debug build. The ones under release/main/res/values will show up in your release build.
PS: You can override all res and asset data like this according to buildTypes and flavor. You can't override Java files though, you could however add them.
As #Aditya Naik said it is possible using Flavors.
Official doc says
BuildType -> Flavor -> main -> Dependencies.
This means that if a resource is declared in both the Build Type and in main, the one from Build Type will be selected.
Note that for the scope of the merging, resources of the same (type, name) but different qualifiers are handled separately.
This means that if src/main/res has
res/layout/foo.xml
res/layout-land/foo.xml
and src/debug/res has
res/layout/foo.xml
Then the merged resource folder will contain the default foo.xml from src/debug/res but the landscape version from src/main/res
for more info visit Official doc - Resource Merging
It is not possible to change the string value after creation of the apk.
But you can assing the value to text or edittext ... etx dynamically after creation of the apk.
For those who come here looking for some way to apply a similar method to raw resources, I dealt with it using buildConfigField.
gradle
...
buildTypes {
debug {
...
buildConfigField "int", "shared_resource_name", 'R.raw.debug_resource_name'
...
}
prod {
...
buildConfigField "int", "shared_resource_name", 'R.raw.prod_resource_name'
...
}
}
Pay attention to the quotes. After that, place BuildConfig.shared_resource_name in the files wherever R.raw.resource_value used to be accessed directly.
This can be used to other resources I think.
I want to do something like that: gradle build DemoApp, and have DemoApp.apk with application name "DemoApp" as output.
Also, can I change application icon if with the same flow?
And some inner parameters?
Product flavors - not a secret for me. I do not need different configurations, I need to change application name at the build time. For example, I have some Rest API, that allows to pass parameter and return apk with application named as that parameter. The same with icon and other.
Checkout Manifest Merger specifically placeholders. Below is an example of setting the app name in gradle file.
// AndroidManifest.xml
<application
android:label="${applicationName}"
// build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: "MobileWorkforce"]
}
The other option is to use #string/app_name and define different string.xml files based upon buildType/flavor.
Given that you don't know how to use flavours properly, a mix of other solutions.
AndroidManifest.xml
<application
android:label="${applicationName}"
MyHttpThing.java
callserver(BuildConfig.ENDPOINT + "/api/v3/", "stuff")
build.gradle
buildTypes {
release {
minifyEnabled false
manifestPlaceholders = [applicationName: myappname]
buildConfigField "String", "ENDPOINT", myendpoint
}
and call with
gradle -Pmyappname=namy_name_name -Pmyendpoint="http://google.com" build DemoApp
Looks like you're looking for Product Flavors, they're exactly for having multiple customized versions of the app with shared codebase. You can customize the icon, as long as any other resource. And yes, the .apk will be named according to the name of the flavor.
We're building an Android app that connects to the cloud. We have a test URL for our APIs and a production URL. We connect the app to our local development machines to talk to the database when developing but find ourselves modifying a global API URL to the production URL every time we generate an APk for the Play Store.
Is there a better way to manage environments for Android? Can we also have two versions of the app (development version) and the Play Store version? I am not able to have two versions as both the apps have the same signature. How do we best manage this?
With android studio and gradle its simple now.
inside your app build.gradle edit signing configs
signingConfigs {
debug {
storeFile file("debug.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
prod {
storeFile file("prod.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
dev {
storeFile file("dev.keystore")
storePassword "..."
keyAlias "..."
keyPassword "..."
}
}
add buildTypes
buildTypes {
debug {
buildConfigField 'String', 'BASE_URL', '"http://127.0.0.1:8080/"'
......
signingConfig signingConfigs.debug
}
prod {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField 'String', 'BASE_URL', '"http://prod.example.com"'
......
signingConfig signingConfigs.prod
}
dev {
buildConfigField 'String', 'BASE_URL', '"http://dev.example.com"'
......
signingConfig signingConfigs.dev
}
}
In your code take base url configured in gradle file by this code.
public final static String BASE_URL = BuildConfig.BASE_URL;
You can also put different KEY or whatever which is build type specific in gradle file and in code it will take according to the build type you are running.
Its even possible to have different package name.
productFlavors {
my_prod {
applicationId "com.example.packtwo"
}
my_dev {
applicationId "com.example.packone"
}
}
In recent gradle config, there are some updates in specifying package name. You have to add flavourDimensions if using productFlavours. See below code with added flavourDimensions
flavorDimensions "pack"
productFlavors {
flavor_dev {
applicationId 'com.example.packtwo'
dimension "pack"
}
flavor_prod {
applicationId 'com.example.packone'
dimension "pack"
}
}
This will give you more details about product flavours and dimensions
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
Check for more possibilities...
But if you are using different flavors you might have to deal with Manifest merging and all.
This is can be achieved using product flavours.
For achieving this requirement:
First of all, Create 2 files under the app folder of your project say development.props and production.props. Or you can add these 2 files in a package under app folder say config.
Basically, these 2 files contain keys and values. This key is same for both files. But their values are different. These files contain one key say “SERVER_URL” and its value. It would be written like this:
SERVER_URL=”Server_url_value”
In this case, only URL is different. So, I have added only one key-value pair in Props file. You can add more.
Then, create ProductFlavours in the app build.gradle file say development and production. Now, access different props files containing URLs in their correseponding flavours like this:
productFlavors {
development {
getProps('./config/development.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
production {
getProps('./config/production.props').each { p ->
buildConfigField 'String', p.key, p.value
}
}
}
def getProps(path) {
Properties props = new Properties()
props.load(new FileInputStream(file(path)))
return props
}
Now, For each flavour, there is a build type And this BuildType is added in app build.gradle. For example, Build type is Debug and release. And I have two flavours i.e. development and production. So, gradle task will be created using both flavour and build type like this:
assemble{flavourName}{BuildType}
Now, you need to type these commands only. It would generate required APK with its corresponding URL. Commands are:
./gradlew assembleProductionRelease would generate release build with Production URL.
./gradlew assembleDevelopmentDebug would generate debug build with Development URL.
./gradlew assembleProductionDebug would generate debug build with Production URL.
./gradlew assembleDevelopmentRelease would generate release build with development URL.
Top three gradle task would be very helpful. But the last task would generate Release build with development URL. But this is not recommended. So, we should stop developer to execute this task i.e. ./gradlew assembleDevelopmentRelease
Now To restrict developer to generate release build using Development URL, add this snippet in your app build.gradle file:
android.variantFilter { variant ->
if(variant.buildType.name.equals('release')
&& variant.getFlavors().get(0).name.equals('development')) {
variant.setIgnore(true);
}
}
Now, If we try to execute task i.e. ./gradlew DevelopmentRelease. Gradle would stop generating the build and throw exception and would say: This task assembleDevelopmentRelease is not found in the root project.
Use Ant to build at least the production versions. This way you can set certain config values/flags during building. Let's say you have a config.xml file that contains the URL to the server. You can have different Ant build targets that will change the URL to point to the appropriate server. Check out this tutorial. It explains exactly how that is done.
This I think is considered as the bast practice in case you use android studio with gradle.
You may want to look at this article: http://tulipemoutarde.be/2013/10/06/gradle-build-variants-for-your-android-project.html
Also available in youtube video: https://www.youtube.com/watch?v=7JDEK4wkN5I
This also allows you to have two different package name for the same app.
It uses gradle flavors to achieve exactly what you are looking for and is very easy to implement.
You can try gradle buildType and productFlavor. It will allow you to specifiy different Environment variables like url, versionName, etc And applicationId which will allow you to have dev and prod builds.
For more details http://developer.android.com/tools/building/configuring-gradle.html
I don't know what's the best practice in that case, but I do like this:
You could make your app a LIB and create 2 apps: production app and testing app. Import your lib for those apps and build their manifests (it's almost copy paste of the old one). Then you replace your /res/ files that are different in each app... (you could create a config.xml file that have the URL).