Localizing string resources added via build.gradle using "resValue" - android

This is in continuation to an answer which helped me on this post
We can add the string resource as follows from build.gradle:
productFlavors {
main{
resValue "string", "app_name", "InTouch Messenger"
}
googlePlay{
resValue "string", "app_name", "InTouch Messenger: GPE Edition"
}
}
It works like a charm and serves the purpose of having different app names per flavor. (with the original app_name string resource deleted from strings.xml file.
But, how do we add localized strings for this string resource added from build.gradle ?
Is there an additional parameter we can pass specifying the locale?
OR
Possible to do it using a gradle task?
Note: I cannot do this using strings.xml (not feasible because of several ways in which my project is structured)

My other answer about the generated resources may be an overkill for you use case though. Base what I currently know about your project I think this one is a better fit:
(not that you can still combine this with generated resources)
src/flavor1/res/values/strings.xml
<string name="app_name_base">InTouch Messenger"</string>
<string name="app_name_gpe">InTouch Messenger: GPE Edition"</string>
src/flavor1/res/values-hu/strings.xml
<string name="app_name_base">InTouch Üzenetküldő"</string>
<string name="app_name_gpe">InTouch Üzenetküldő: GPE Változat"</string>
src/flavor2/res/values/strings.xml
<string name="app_name_base">Whatever Messenger"</string>
<string name="app_name_gpe">Whatever Messenger: GPE Edition"</string>
src/flavor2/res/values-hu/strings.xml`
<string name="app_name_base">Whatever Üzenetküldő"</string>
<string name="app_name_gpe">Whatever Üzenetküldő: GPE Változat"</string>
build.gradle
android {
sourceSets {
[flavor1, flavor3].each {
it.res.srcDirs = ['src/flavor1/res']
}
[flavor2, flavor4].each {
it.res.srcDirs = ['src/flavor2/res']
}
}
productFlavors { // notice the different numbers than sourceSets
[flavor1, flavor2].each {
it.resValue "string", "app_name", "#string/app_name_base"
}
[flavor3, flavor4].each {
it.resValue "string", "app_name", "#string/app_name_gpe"
}
}
}
This means that flavor1/2 will have an extra unused app_name_gpe string resource, but that'll be taken care of by aapt:
android {
buildTypes {
release {
shrinkResources true // http://tools.android.com/tech-docs/new-build-system/resource-shrinking
}

If you do not have to operate on those strings, the best option is moving to strings.xml, but that would make you share all res folder between flavors.
If you generate these strings based on some property on build.gradle, then I think you're out of luck, unfortunately.
EDIT: clarifying what I mean by operate above and add some options:
By operating on those strings I mean some sort of concatenation with a build parameter, a reading from command line or environment variable during the build process (e.g., getting the commit SHA1 so that it's easier to trace bugs later). If no operation is necessary, strings.xml may be an option. But when you overwrite a res folder for flavor, all of it is overwritten and that could pose a problem if several flavors share the same res except for a limited number of strings.
If each APK has its own locale, then it's just a resValue or buildConfigField in a flavor. You can define variables to for easier reuse of values. Something like
def myVar = "var"
...
flavor1 {
resValue "string", "my_res_string", "${myVar}"
}
flavor2 {
resValue "string", "my_res_string", "${myVar}"
}
But if several locales are needed in the same APK and it will be chosen at runtime by Android, then the string must be in the correct values-<locale> folder.

You're operating on different levels here, BuildConfig is code, and as such not localized, that's why we have Lint warnings for hard-coded strings. Localization in Android is done via <string resources, there's no way around that if you want the system to choose the language at runtime depending on user settings. There are many ways to have resources though: values folder, resValue in build.gradle, and generated resources.
You should look into the buildSrc project in Gradle, for example I use it to generate SQL Inserts from src/main/values/stuff.xml. Here's some code to start with.
buildSrc/build.gradle
// To enable developing buildSrc in IDEA import buildSrc/build.gradle as a separate project
// Create a settings.gradle in buildSrc as well to prevent importing as subproject
apply plugin: 'groovy'
repositories { jcenter() }
dependencies {
compile localGroovy()
compile gradleApi()
testCompile 'junit:junit:4.12'
}
buildSrc/src/main/groovy/Plugin.groovy
import org.gradle.api.*
/**
* Use it as
* <code>
* apply plugin: MyPlugin
* myEntities {
* categories {
* input = file(path to Android res xml with Strings)
* output = file(path to asset SQL file)
* conversion = "structure|SQL"
* }
* }
* </code>
*/
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
def entities = project.container(MyEntity)
// this gives the name for the block in build.gradle
project.extensions.myEntities = entities
def allTasks = project.task('generateYourStuff')
def allTasksClean = project.task('cleanGenerateYourStuff')
project.afterEvaluate {
entities.all { entity ->
//println "Creating task for ${entity.name} (${entity.input} --${entity.conversion}--> ${entity.output})"
def task = project.task(type: GenerateTask, "generateYourStuff${entity.name.capitalize()}") {
input = entity.input
output = entity.output
conversion = entity.conversion
}
allTasks.dependsOn task
// clean task is automagically generated for every task that has output
allTasksClean.dependsOn "clean${task.name.capitalize()}"
}
}
}
}
class MyEntity {
def input
def output
String conversion
final String name
MyEntity(String name) {
this.name = name
}
}
buildSrc/src/main/groovy/GenerateTask.groovy
import net.twisterrob.inventory.database.*
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.*
class GenerateTask extends DefaultTask {
#InputFile File input
#OutputFile File output
#Optional #Input String conversion
#TaskAction void generate() {
input.withReader { reader ->
// you may need to treat output as a folder
output.parentFile.mkdirs()
output.withWriter { writer ->
// custom transformation here read from reader, write to writer
}
}
}
}
This is just the skeleton you can go wild and do anything from here: e.g. retrieve a CSV through the network and spread the contents into generated variant*/res/values-*/gen.xml files.
You can run it manually when you need to or run it at the right point in the build lifecycle (in build.gradle:
android.applicationVariants.all { com.android.build.gradle.api.ApplicationVariant variant ->
variant.mergeAssets.dependsOn tasks.generateYourStuff
}

Related

BuildConfigField mock for unit test in Kotlin

I'm trying to cover as much as possible a Kotlin Android library and I'm encountering an issue about custom BuildConfig variable, better known as buildConfigField.
I would like to mock this variable to test both true and false values.
Extract from Gradle file :
android {
defaultConfig {
buildConfigField "boolean", "ENABLE_LOG", "false"
}
flavorDimensions "log"
productFlavors {
loggable {
buildConfigField "boolean", "ENABLE_LOG", "true"
dimension "log"
}
notloggable {
dimension "log"
}
}
}
Extract of the Kotlin function to be tested :
fun buildClient(): MyClient {
var myClientBuilder : MyClient.Builder = MyClient.Builder();
if (BuildConfig.ENABLE_LOG) {
val interceptor = LoggingInterceptor();
interceptor.setLevel(LoggingInterceptor.Level.ALL);
myClientBuilder.addInterceptor(interceptor);
}
return myClientBuilder.build()
}
Unit test :
#Test
fun buildClient_enableLog_oneInterceptor() {
// GIVEN
Mockito.mock(BuildConfig::class.java)
Mockito.doReturn(true).`when`(BuildConfig.ENABLE_LOG)
// WHEN
val myClient = myService!!.buildClient()
// THEN
assertNotNull(myClient)
assertNotNull(myClient.interceptors())
assertEquals(1, myClient.interceptors().size)
}
I tried different things and it never works.
If someone have already done this work, it can help me a lot (and others I guess).
Thanks
ReflectionHelpers.setStaticField(BuildConfig::class.java, "ENABLE_LOG", true)
By default, all tests run against the debug build type. You can change this to another build type by using the testBuildType property in your module-level build.gradle file. For example, if you want to run your tests against your "staging" build type, edit the file as shown in the following snippet.
android {
...
testBuildType "staging"
}
but this is causing other options to fail
Little late to the party but this is how you should test any thing related to BuildConfig file.
BuildConfig.java is generated for each variant of your app. In your case you have atleast 4 variants.
LoggableDebug
LoggableRelease
NotloggableDebug
NotloggableRelease
ENABLE_LOG will be false for options 3 and 4.
If you want to Unit test this, I recommend writing UnitTest in src/testNotLoggable/java/com/.../TestFile.java.
In that TestFile.java your BuildConfig.ENABLE_LOG should be false.
You can check BuildConfig.java file for each variant under /build/source/buildConfig/flavorname/debug/com/project/../BuildConfig.java

Gradle SourceSets by productFlavor and buildType

EDIT flavors and paths:
Currently I have:
sourceSets.whenObjectAdded {
sourceSet ->
def sourceData = rootProject.ext[sourceSet.name]
sourceSet.java.srcDirs = sourceData.javaDirRelease
}
The rootProject.ext is a file where all the productFlavor specific configuration is defined like this:
ext{
flavor1 = [
javaDirRelease : ['src/pathToJavaReleaseFiles']
javaDirDebug : ['src/pathToJavaDebugFiles']
]
}
In the main build.gradle I also do: apply from: 'variants.gradle' which contains the above ext{} object.
The sourceSets are defined as such:
sourceSets {
flavor1{}
}
This works but I want to do add a sourceSet specific to productFlavor and the buildType like this:
sourceSet.debug.java.srcDirs = 'src/pathToJavaDebugFiles'
Which could be defined for each product flavor and per buildType, but this doesn't work when I try to add it dynamically.
What works for me is this (thanks to this answer How can I specify per flavor buildType sourceSets?):
sourceSets {
flavor1{
def flavorData = rootProject.ext['flavor1']
release {
java.srcDirs = flavorData.javaDirRelease
}
debug {
java.srcDirs = flavorData.javaDirDebug
}
}
}
However I would really like this to be added dynamically, so I can still preserve my configuration file intact. My build configuration is quite complex and not as simple as described here, therefore I don't need a suggestion to put the source files into a folder src/flavor1Debug because this resources are used from other productFlavors also, so this won't work.
I think I understand. I will say first off once again that this is not a great setup and you should really look to move towards a more standard setup.
So for the solution, in order to read flavor sources dynamically, I recommend moving your ext structure to something like this:
ext {
sources = [
flavor1Debug : ['src/pathToJavaReleaseFiles']
flavor1Release : ['src/pathToJavaDebugFiles']
]
}
This lets you read the source paths and iterate over keys and values in project.ext.sources.
Then you can find the variant by name and add sources from the array.
applicationVariants.all { variant ->
def extraSources = project.ext.sources.get(variant.name)
if (extraSources != null) {
def extraSourceFiles = extraSources.collect { project.file(it) }
def sourceSet = variant.sourceSets.find { it.name == variant.name }
sourceSet.java.srcDir extraSourceFiles
def dummyTask = project.task("register${variant.name}ExtraSourcesTask")
variant.registerJavaGeneratingTask(dummyTask, extraSourceFiles)
}
}
The annoyance with doing this at the variant level is that you have to register the extra sources since the variant object at this point has already collected the sources from the source set and product flavors. However, I think this is probably the least invasive way to do this dynamically.

Create and access productFlavor variables in android's build.gradle

I've a multi flavor project which is built by a CI and published to HockeyApp.
Each flavor has an applicationId and an apiToken, which is stored in the flavor itself (to keep all important variables in one place):
def token = null
productFlavors {
prod {
applicationId "de.example.appname"
buildConfigField 'String', 'FLAVOR_ID', '"0"'
buildConfigField 'String', 'HOCKEY_APP_ID', '"1234567890"'
token = "1q2w3e4r5t6z7u8i9o0p"
}
demo {
applicationId "de.example.appname.demo"
buildConfigField 'String', 'FLAVOR_ID', '"1"'
buildConfigField 'String', 'HOCKEY_APP_ID', '"987654321"'
token = "p0o9i8u7z6t5r4e3w2q1"
}
}
On the same level like "productFlavors" there are the hockeyApp-settings:
hockeyapp {
apiToken = token
releaseType = 0
notify = 0
status = 1
notesType = 1
notes = "Uploaded with gradle"
}
For debugging the code I build & upload the .apk-file via terminal:
./gradlew uploadProdReleaseToHockeyApp [...]
Unfortunately the variable token of the prod-flavor is always overridden by the demo-value. So after each uploading process I get errors like
Error response from HockeyApp: App could not be created.
because gradle tries to upload the prod-flavor with the demo-token.
Here some additional basic data:
compileSdkVersion 24
buildToolsVersion "24.0.1"
compile 'net.hockeyapp.android:HockeySDK:4.0.0'
classpath 'com.android.tools.build:gradle:2.1.3'
classpath 'de.felixschulze.gradle:gradle-hockeyapp-plugin:3.5'
Based on my requirements, is there a solution to define flavor-variables and access them in the shown way?
In this special case I found following answer:
Add the hockeyapp-task with your modifications needed
hockeyapp {
apiToken = "not_required"
releaseType = 0
notify = 0
status = 2
teams = 1234
notesType = 1
}
In the next step add flavor-based gradle tasks to modify you hockeyapp's apiToken:
task setDevReleaseApiToken << {
hockeyapp.apiToken = "1234567890abcdefghijklmnopqrstuvwxyz"
}
task setProdReleaseApiToken << {
hockeyapp.apiToken = "1234567890abcdefghijklmnopqrstuvwxyz"
}
These tasks are called in gradle's whenTaskAdded-task, you can simply "override" it like this:
tasks.whenTaskAdded { task ->
if (task.name == 'uploadDevReleaseToHockeyApp') {
task.dependsOn 'setDevReleaseApiToken'
} else if (task.name == 'uploadProdReleaseToHockeyApp') {
task.dependsOn 'setProdReleaseApiToken'
}
}
Everytime the task uploadDevReleaseToHockeyApp is called (manually or by CI..) the task setDevReleaseApiToken is called and the related apiToken is assigned.
Extend this schema for all other flavors if needed!
Though the OP might not be looking for an answer anymore, someone other might stumble upon this thread (like I did when I was dealing with this problem) so I'd like to share a solution.
I was dealing with variables having effect on gradle procedure itself (not on the source code) so I couldn't use resValue or buildConfigField.
This problem can be solved by using the settings.gradle file. This file executes before build.gradle, so you can prepare all your variables using this condition:
include ':project...'
rootProject.name = 'name...'
def VARIANT1 = "YourFlavorName";
if((gradle.startParameter.taskNames.contains(":assemble"+VARIANT1+"Debug"))||
(gradle.startParameter.taskNames.contains(":assemble"+VARIANT1+"Release"))
||
(gradle.startParameter.taskNames.contains(":generate"+VARIANT1+"DebugSources"))||(gradle.startParameter.taskNames.contains(":generate"+VARIANT1+"ReleaseSources"))) {
gradle.ext.variable = value
gradle.ext.var...
}
It is a bit of a work-around but what this does is it checks the build commands given by your Android Studio and searches for the flavor name inside. Conditions with ":generate" are just for Building, ":assebmle" is for Running the application on a device.
Any variable you create in your settings file like this can be accessed from your build.gradle file.
myVariable = gradle.ext.variable
This should do it.

Android Gradle identify current falvor at compile time

How is it possible to identify the current flavor being compiled. I'm trying to add a file to compile only if I'm compiling a certain product flavor.
buildTypes {
android.applicationVariants.all { variant ->
variant.productFlavors.each() { flavor ->
if (flavor.name.equals(currentFlavorName)) {
The problem is that I can't seem to find where the currentFlavourName of the flavor which I am currently building is located.
just put the strings you want for flavor1 into:
src/flavor1/res/values/strings.xml
and the strings for flavor2 into:
src/flavor2/res/values/strings.xml
no need to put logic into your gradle file
Android uses a unique build process regarding your resources for different flavors and it is very easy to control.
if you set up your main source:
project-name
------------/app
---------------/src
-------------------/main
------------------------/res
----------------------------/values
------------------------/java
-------------------/development
-------------------------------/res
-----------------------------------/values
-------------------------------/java
-------------------/production
------------------------------/res
----------------------------------/values
------------------------------/java
This would be a bottom up approach from product flavor into main. Meaning if you have a strings.xml with items having the same name existing in development/res/values and have values that also exist in main/res/values/strings.xml these will be over written (and same would go for the production flavor) based on the build variant defined in your gradle file.
android {
productFlavors {
production {
applicationId "com.test.prod"
versionName "1.0.0"
}
development {
applicationId "com.test.dev"
versionName "1.0.0"
}
}
I don't know if exits a method to get the currentFlavor. I haven't found it yet.
A ugly solution can be
variant.productFlavors.each() { flavor ->
if (flavor.name.equals("flavor1")) {
//..........
}
}
However, if you want to be able to control which strings.xml you are using, you can achieve it in different ways.
First of all you can just define a xml file in your flavor folder.
app/src/main/res/values/ -> for common resources
app/src/flavor1/res/values -> resources for flavor1
app/src/flavor2/res/values -> resources for flavor2
This doesn't require any config in your build.gradle script.
A second option is to define a resource value using build.gradle.
Something like:
productFlavors {
flavor1 {
resValue "string", "app_name", "IRCEnterprise"
}
//....
}
Another option is to create some field in your BuildConfig class using this kind of script.
productFlavors {
flavor1 {
buildConfigField "String", "name", "\"MY FLAVOR NAME\""
}
}

Create Free/Paid versions of Application from same code

So I'm coming down to release-time for my application. We plan on releasing two versions, a free ad-based play-to-unlock version, and a paid fully unlocked version. I have the code set up that I can simply set a flag on startup to enable/disable ads and lock/unlock all the features. So literally only one line of code will execute differently between these versions.
In order to release two separate applications, they require different package names, so my question is this: Is there an easy way to refactor my application's package name? Eclipse's refactoring tool doesn't resolve the generated R file, or any XML references in layout and manifest files. I've attempted to make a new project using the original as source, but I can't reference the assets and resources, and I'm looking to avoid duplicating any of my code and assets. It's not a huge pain to refactor it manually, but I feel there must be a better way to do it. Anybody have an elegant solution to this?
Edit/Answered:
For my situation I find it perfectly acceptable to just use Project -> Android Tools -> Rename Application Package. I wasn't aware this existed, and I feel like an idiot for posting this now. Thanks for everyone's answers and comments, feel free to vote this closed.
It's very simple by using build.gradle in Android Studio. Read about productFlavors. It is a very usefull feature. Just simply add following lines in build.gradle:
productFlavors {
lite {
packageName = 'com.project.test.app'
versionCode 1
versionName '1.0.0'
}
pro {
packageName = 'com.project.testpro.app'
versionCode 1
versionName '1.0.0'
}
}
In this example I add two product flavors: first for lite version and second for full version. Each version has his own versionCode and versionName (for Google Play publication).
In code just check BuildConfig.FLAVOR:
if (BuildConfig.FLAVOR == "lite") {
// add some ads or restrict functionallity
}
For running and testing on device use "Build Variants" tab in Android Studio to switch between versions:
Possibly a duplicate of Bulk Publishing of Android Apps.
Android Library projects will do this for you nicely. You'll end up with 1 library project and then a project for each edition (free/full) with those really just containing different resources like app icons and different manifests, which is where the package name will be varied.
Hope that helps. It has worked well for me.
The best way is to use "Android Studio" -> gradle.build -> [productFlavors + generate manifest file from template]. This combination allows to build free/paid versions and bunch of editions for different app markets from one source.
This is a part of templated manifest file:
<manifest android:versionCode="1" android:versionName="1" package="com.example.product" xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="#drawable/ic_launcher"
android:label="#string/{f:FREE}app_name_free{/f}{f:PAID}app_name_paid{/f}"
android:name=".ApplicationMain" android:theme="#style/AppTheme">
<activity android:label="#string/{f:FREE}app_name_free{/f}{f:PAID}app_name_paid{/f}" android:name=".ActivityMain">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
This is template "ProductInfo.template" for java file: ProductInfo.java
package com.packagename.generated;
import com.packagename.R;
public class ProductInfo {
public static final boolean mIsPaidVersion = {f:PAID}true{/f}{f:FREE}false{/f};
public static final int mAppNameId = R.string.app_name_{f:PAID}paid{/f}{f:FREE}free{/f};
public static final boolean mIsDebug = {$DEBUG};
}
This manifest is processed by gradle.build script with productFlavors and processManifest task hook:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
...
android {
...
productFlavors {
free {
packageName 'com.example.product.free'
}
paid {
packageName 'com.example.product.paid'
}
}
...
}
afterEvaluate { project ->
android.applicationVariants.each { variant ->
def flavor = variant.productFlavors[0].name
tasks['prepare' + variant.name + 'Dependencies'].doLast {
println "Generate java files..."
//Copy templated and processed by build system manifest file to filtered_manifests forder
def productInfoPath = "${projectDir}/some_sourcs_path/generated/"
copy {
from(productInfoPath)
into(productInfoPath)
include('ProductInfo.template')
rename('ProductInfo.template', 'ProductInfo.java')
}
tasks.create(name: variant.name + 'ProcessProductInfoJavaFile', type: processTemplateFile) {
templateFilePath = productInfoPath + "ProductInfo.java"
flavorName = flavor
buildTypeName = variant.buildType.name
}
tasks[variant.name + 'ProcessProductInfoJavaFile'].execute()
}
variant.processManifest.doLast {
println "Customization manifest file..."
// Copy templated and processed by build system manifest file to filtered_manifests forder
copy {
from("${buildDir}/manifests") {
include "${variant.dirName}/AndroidManifest.xml"
}
into("${buildDir}/filtered_manifests")
}
tasks.create(name: variant.name + 'ProcessManifestFile', type: processTemplateFile) {
templateFilePath = "${buildDir}/filtered_manifests/${variant.dirName}/AndroidManifest.xml"
flavorName = flavor
buildTypeName = variant.buildType.name
}
tasks[variant.name + 'ProcessManifestFile'].execute()
}
variant.processResources.manifestFile = file("${buildDir}/filtered_manifests/${variant.dirName}/AndroidManifest.xml")
}
}
This is separated task to process file
class processTemplateFile extends DefaultTask {
def String templateFilePath = ""
def String flavorName = ""
def String buildTypeName = ""
#TaskAction
void run() {
println templateFilePath
// Load file to memory
def fileObj = project.file(templateFilePath)
def content = fileObj.getText()
// Flavor. Find "{f:<flavor_name>}...{/f}" pattern and leave only "<flavor_name>==flavor"
def patternAttribute = Pattern.compile("\\{f:((?!${flavorName.toUpperCase()})).*?\\{/f\\}",Pattern.DOTALL);
content = patternAttribute.matcher(content).replaceAll("");
def pattern = Pattern.compile("\\{f:.*?\\}");
content = pattern.matcher(content).replaceAll("");
pattern = Pattern.compile("\\{/f\\}");
content = pattern.matcher(content).replaceAll("");
// Build. Find "{$DEBUG}" pattern and replace with "true"/"false"
pattern = Pattern.compile("\\{\\\$DEBUG\\}", Pattern.DOTALL);
if (buildTypeName == "debug"){
content = pattern.matcher(content).replaceAll("true");
}
else{
content = pattern.matcher(content).replaceAll("false");
}
// Save processed manifest file
fileObj.write(content)
}
}
Updated: processTemplateFile created for code reusing purposes.
Gradle allows to use generated BuildConfig.java to pass some data to code.
productFlavors {
paid {
packageName "com.simple.paid"
buildConfigField 'boolean', 'PAID', 'true'
buildConfigField "int", "THING_ONE", "1"
}
free {
packageName "com.simple.free"
buildConfigField 'boolean', 'PAID', 'false'
buildConfigField "int", "THING_ONE", "0"
}
For everyone who want to use the solution by Denis:
In the new gradle version packageName is now applicationId and don't forget to put productFlavors { ... } in android { ... }
productFlavors {
lite {
applicationId = 'com.project.test.app'
versionCode 1
versionName '1.0.0'
}
pro {
applicationId = 'com.project.testpro.app'
versionCode 1
versionName '1.0.0'
}
}
One approach I'm experimenting with is using fully-qualified names for activities, and just changing the package attribute. It avoids any real refactoring (1 file copy, 1 text sub).
This almost works, but the generated R class isn't picked up, as the package for this is pulled out of AndroidManifest.xml, so ends up in the new package.
I think it should be fairly straight forward to build AndroidManifest.xml via an Ant rule (in -pre-build) that inserts the distribution package name, and then (in -pre-compile) the generated resources into the default (Java) package.
Hope this helps,
Phil Lello
If you want another application name, depending of the flavor, you can also add this:
productFlavors {
lite {
applicationId = 'com.project.test.app'
resValue "string", "app_name", "test lite"
versionCode 1
versionName '1.0.0'
}
pro {
applicationId = 'com.project.testpro.app'
resValue "string", "app_name", "test pro"
versionCode 1
versionName '1.0.0'
}
}

Categories

Resources