Edited at

buildType/productFlavorsでアプリ名を分ける方法

More than 1 year has passed since last update.

applicationIdSuffixでデバッグ時のみ".debug"とか追加していると、debug用アプリとrelease用アプリを同端末に同時に存在させられて便利なのだが、同じアプリ名だとどっちがデバッグ用か分からなくなってしまうので、アプリ名でも見分けたいよねというお話。


最終目標

debug用アプリには、(d)をアプリ名のprefixに付ける。

例)

デバッグ用=(d)私のアプリ

リリース用=私のアプリ

勿論「私のアプリ(d)」というのも出来るが、これだと長いアプリ名の時にホーム画面で見えなかったりするので、prefixにするのが私流です^^;


前提

AndroidStudio 1.3

Gradleのビルドバージョンも1.3.0にして下さい。


build.gradle(project)

dependencies {

classpath 'com.android.tools.build:gradle:1.3.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}



方法


1. productFlavorsがなく、buildTypeのみで分ける方法。

app/src/debug/res/values/strings.xml

app/scr/release/res/values/strings.xml

上記でapp_nameを書き分けておけばOK。

言語別にあるなら言語別valuesフォルダを用意して。

当然ながら、manifestファイルでlabelに指定しておいてね。


AndroidManifest.xml

<application

android:label="@string/app_name"
省略


2. productFlavorsがあり、全flavorsでアプリ名が同じ場合。

あまりそんなパターンは無いかと思いますが、1.と同じ方法をとればOK.


3. productFlavors毎にアプリ名が異なり、buildType別には分けない場合。

app/src/flavor1/res/values/strings.xml

app/scr/flavor2/res/values/strings.xml

上記ようにflavor毎のxmlで書き分けておけばOK。

言語別にあるなら言語別valuesフォルダを用意する。


4. productFlavor毎ににアプリ名が異なり、buildType別にも分ける場合。

2017/04/07追記

この方法より、manifestPlaceholderを使った方法のほうがスマートかも。まとまったら新しく記事を書きます。

これがやっかいだった。

例えば・・・

flavor1 : app_name = "私のアプリ1" applicationId = "my.app.flavor1"

flavor2 : app_name = "私のアプリ2" applicationId = "my.app.flavor2"

となっている場合、最終目標は、

flavor1Debug : app_name = "(d)私のアプリ1"

flavor1Release : app_name = "私のアプリ1"

flavor2Debug : app_name = "(d)私のアプリ2"

flavor2Release : app_name = "私のアプリ2"

となることである。

これは、一筋縄ではいかない。


(1) 各flavor毎にstrings.xmlでapp_nameを定義する

これは問題あるまい。


(2) debugビルドのmergeResourcesタスク内で、merge済みxmlを強制的に書き換える。

こちらで見つけた方法。

http://stackoverflow.com/questions/22396346/setting-app-label-based-on-buildvariant

ググったときに他にもいくつか手段はあったが、言語別にvaluesフォルダがある場合は、これしか適用できなかった。

まず、app/build.gradleに以下のメソッド?を追加する


build.gradle(app)

// debug用アプリの名称に(d)を付ける

def debugAppRename(variant, labelResource, lang) {
def flavor = variant.flavorName
def buildtype = variant.buildType
// Append buildType name to app label
if (buildtype.debuggable) {
variant.mergeResources << {
def valuesFile = "$buildDir/intermediates/res/merged/${flavor}/${buildtype.name}"
if (lang.length()==0) {
// default言語
valuesFile = valuesFile + "/values/values.xml"
} else {
// 言語別
valuesFile = valuesFile + "/values-${lang}/values-${lang}.xml"
}
def values = (new XmlParser()).parse(valuesFile)
values.string.each { m ->
def index = m.text().indexOf("(d)")
if (index == -1) {
m.value = "(d)${m.text()}"
new XmlNodePrinter(new PrintWriter(new FileWriter(valuesFile)))
.print(values)
}
}
}
}
}

android{
省略
}


続いて、android{}内に以下の記述をする。


build.gradle(app)

android{

省略

applicationVariants.all { variant ->
debugAppRename(variant, 'app_name', "")
debugAppRename(variant, 'app_name', "ja")
}
}


debugAppRenameを言語数分呼び出す。

なお、debug以外では置き換えられないのでご安心を。

ちなみに、私は「release用apkのファイル名を変更して特定フォルダにコピーする」というのも書いているので、最終的なbuild.gradleはこんな感じ。


build.gradle(app)

apply plugin: 'com.android.application'

// debug用アプリの名称に(d)を付ける
def debugAppRename(variant, labelResource, lang) {
def flavor = variant.flavorName
def buildtype = variant.buildType
// Append buildType name to app label
if (buildtype.debuggable) {
variant.mergeResources << {
def valuesFile = "$buildDir/intermediates/res/merged/${flavor}/${buildtype.name}"
if (lang.length()==0) {
// default言語
valuesFile = valuesFile + "/values/values.xml"
} else {
// 言語別
valuesFile = valuesFile + "/values-${lang}/values-${lang}.xml"
}
def values = (new XmlParser()).parse(valuesFile)
values.string.each { m ->
if (m.@name == labelResource) {
def index = m.text().indexOf("(d)")
if (index == -1) {
m.value = "(d)${m.text()}"
new XmlNodePrinter(new PrintWriter(new FileWriter(valuesFile)))
.print(values)
}
}
}
}
}
}

android {
signingConfigs {
config {
keyAlias 'aaaa'
keyPassword 'bbbb'
storeFile file('app.jks')
storePassword 'cccc'
}
}
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "my.app"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
}
debug {
applicationIdSuffix '.debug'
}
}
productFlavors {
flavor1 {
applicationId 'my.app.flavor1'
versionCode 1
versionName '1.0.0'
}
flavor2 {
applicationId 'my.app.flavor2'
versionCode 1
versionName '1.0.0'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

// flavorName_r(versionCode)_v(versionName)_timestamp.apk というファイル名でapkを出力
// gradle.properties内のdeployToを任意のフォルダに書き換えると、そのフォルダにapkが出力されます
applicationVariants.all { variant ->
if (variant.buildType.name.equals("release")) {
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
// Rename APK
def versionCode = variant.versionCode
def versionName = variant.versionName
def appName = variant.flavorName
def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
def newName = "MyApp_${appName}_r${versionCode}_v${versionName}_${date}.apk"

def publish = project.tasks.create("publish_${appName}")

// Move and Rename APK
def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
task.from(output.outputFile)
task.rename(output.outputFile.name, newName)
task.into(deployTo)

task.dependsOn variant.assemble
publish.dependsOn task
}
}
}
debugAppRename(variant, 'app_name', "")
debugAppRename(variant, 'app_name', "ja")
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])

compile 'com.android.support:support-v4:23.+'
compile 'com.google.android.gms:play-services-analytics:7.8.+'
}



gradle.properties

# apkをコピーしたいフォルダを記述

deployTo=../../out


余談

これまでのproductFlavorに関する調査で、かなりのことができるようになった。

AndroidStudio1.3でproductFlavor

特定のproductFlavor/buildTypeにだけActivityを追加する

特定のproductFlavor/buildTypeでだけ使用するライブラリプロジェクトを作る

ルートフォルダの異なるプロジェクト/moduleをimportせずに参照する

あとは、publishXXXを全flavorに対して自動的に呼べるタスクが書けると更に楽になりそうだ。

今は、以下のようなshを書いてターミナルから実行しているが、Gradleタスクに追加する方法をご存じでしたら教えて頂きたく^^;


publishAllFlavors.sh

#!/bin/bash

./gradlew publish_flavor1
./gradlew publish_flavor2



追記1(2015/08/31)

app/build.gradleが長くなったので分離してみた。

(projRoot)

+ scripts/

  + publish.gradle

+ app/

上記のようにスクリプトを配置。


scripts/publish.gradle

// debug用アプリの名称に(d)を付ける

def debugAppRename(variant, labelResource, lang) {
def flavor = variant.flavorName
def buildtype = variant.buildType
// Append buildType name to app label
if (buildtype.debuggable) {
variant.mergeResources << {
def valuesFile = "$buildDir/intermediates/res/merged/${flavor}/${buildtype.name}"
if (lang.length() == 0) {
// default言語
valuesFile = valuesFile + "/values/values.xml"
} else {
// 言語別
valuesFile = valuesFile + "/values-${lang}/values-${lang}.xml"
}
def values = (new XmlParser()).parse(valuesFile)
values.string.each { m ->
if (m.@name == labelResource) {
def index = m.text().indexOf("(d)")
if (index == -1) {
m.value = "(d)${m.text()}"
new XmlNodePrinter(new PrintWriter(new FileWriter(valuesFile)))
.print(values)
}
}
}
}
}
}

android {
// (appName)_(flavorName)_r(versionCode)_v(versionName)_timestamp.apk というファイル名でapkを出力
// gradle.properties内の【appName】を書き換える
// gradle.properties内の【deployTo】を任意のフォルダに書き換えると、そのフォルダにapkが出力されます
applicationVariants.all { variant ->
if (variant.buildType.name.equals("release")) {
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
// Rename APK
def versionCode = variant.versionCode
def versionName = variant.versionName
def flavorName = variant.flavorName
//noinspection UnnecessaryQualifiedReference
def date = new java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
def newName = "${appName}_${flavorName}_r${versionCode}_v${versionName}_${date}.apk"

def publish = project.tasks.create("publish_${flavorName}")

// Move and Rename APK
def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
//noinspection GroovyAssignabilityCheck
task.from(output.outputFile)
//noinspection GroovyAssignabilityCheck
task.rename(output.outputFile.name, newName)
task.into(deployTo)

//noinspection GroovyAssignabilityCheck
task.dependsOn variant.assemble
publish.dependsOn task
}
}
}
debugAppRename(variant, 'app_name', "")
debugAppRename(variant, 'app_name', "ja")
}
}



build.gradle(app)

apply plugin: 'com.android.application'

apply from: '../scripts/publish.gradle'

android {
compileSdkVersion 22
buildToolsVersion "22.0.1"

lintOptions {
abortOnError false
}

signingConfigs {
config {
keyAlias 'aaaa'
keyPassword 'bbbb'
storeFile file('app.jks')
storePassword 'cccc'
}
}
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "my.app"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
}

buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
debuggable false
//noinspection GroovyAssignabilityCheck
signingConfig signingConfigs.release
}
debug {
applicationIdSuffix '.debug'
}
}

productFlavors {
flavor1 {
applicationId 'my.app.flavor1'
versionCode 1
versionName '1.0.0'
}
flavor2 {
applicationId 'my.app.flavor2'
versionCode 1
versionName '1.0.0'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

}

dependencies {
compile 'com.android.support:support-v4:23.+'
compile 'com.google.android.gms:play-services-analytics:7.8.+'
}



gradle.properties

# apkをコピーしたいフォルダを記述

deployTo=../../out

# publishタスクで出力するapkのファイル名Prefix
appName = MyAppName


すっきりしたうえにpublish.gradleを使い回せるようになって更に便利。