Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>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.</p> <hr> <p>This is a part of templated manifest file:</p> <hr> <pre><code>&lt;manifest android:versionCode="1" android:versionName="1" package="com.example.product" xmlns:android="http://schemas.android.com/apk/res/android"&gt; &lt;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"&gt; &lt;activity android:label="@string/{f:FREE}app_name_free{/f}{f:PAID}app_name_paid{/f}" android:name=".ActivityMain"&gt; &lt;intent-filter&gt; &lt;action android:name="android.intent.action.MAIN"/&gt; &lt;category android:name="android.intent.category.LAUNCHER"/&gt; &lt;/intent-filter&gt; &lt;/activity&gt; &lt;/application&gt; </code></pre> <hr> <p>This is template "ProductInfo.template" for java file: ProductInfo.java</p> <hr> <pre><code> 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}; } </code></pre> <hr> <p>This manifest is processed by gradle.build script with <strong>productFlavors</strong> and <strong>processManifest</strong> task hook:</p> <hr> <pre><code>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 -&gt; android.applicationVariants.each { variant -&gt; 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") } } </code></pre> <hr> <p>This is separated task to process file</p> <hr> <pre><code>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:&lt;flavor_name&gt;}...{/f}" pattern and leave only "&lt;flavor_name&gt;==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) } } </code></pre> <p><strong>Updated:</strong> processTemplateFile created for code reusing purposes. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload