59

So to change the generated APK filename inside gradle android I could do something like:

applicationVariants.output.all {
    outputFileName = "the_file_name_that_i_want.apk"
}

Is there a similar thing for the generated App Bundle file? How can I change the generated App Bundle filename?

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
Archie G. Quiñones
  • 11,638
  • 18
  • 65
  • 107

7 Answers7

83

You could use something like this:

defaultConfig {
  applicationId "com.test.app"
  versionCode 1
  versionName "1.0"
  setProperty("archivesBaseName", applicationId + "-v" + versionCode + "(" + versionName + ")")
}
SaXXuM
  • 946
  • 1
  • 5
  • 7
29

As a more generic way to Martin Zeitlers answer the following will listen for added tasks, then insert rename tasks for any bundle* task that gets added.

Just add it to the bottom of your build.gradle file.

Note: It will add more tasks than necessary, but those tasks will be skipped since they don't match any folder. e.g. > Task :app:renameBundleDevelopmentDebugResourcesAab NO-SOURCE

tasks.whenTaskAdded { task ->
    if (task.name.startsWith("bundle")) {
        def renameTaskName = "rename${task.name.capitalize()}Aab"
        def flavor = task.name.substring("bundle".length()).uncapitalize()
        tasks.create(renameTaskName, Copy) {
            def path = "${buildDir}/outputs/bundle/${flavor}/"
            from(path)
            include "app.aab"
            destinationDir file("${buildDir}/outputs/renamedBundle/")
            rename "app.aab", "${flavor}.aab"
        }

        task.finalizedBy(renameTaskName)
    }
}
David Medenjak
  • 33,993
  • 14
  • 106
  • 134
  • 2
    Hi, I have tried this one but this is not working in my scenario, As i have multiple flavours, can you help what should i do ? – Abdul Momen Khan Feb 25 '19 at 13:49
  • 1
    It is not working for me if I use 'Build -> Generate signed Bundle'. But it is working if you build using terminal. (following command 'gradlew :app:bundleFlavor') – Allinone51 Apr 05 '19 at 08:16
  • If this doesn't work, your include probably doesn't match. Try to remove include and rename then see if this copies anything. – kuhnroyal Nov 14 '19 at 16:42
  • Is there really no better way to find the original path of the aab file? – natronite Nov 20 '19 at 16:05
  • 2
    Not working right now because Android Studio has built-in tasks rename "rename${task.name.capitalize()}Aab". – Alex Misiulia Nov 25 '19 at 13:40
  • See this answer for how to include flavors and build types in the file name https://stackoverflow.com/a/69305535/1159930 – Markymark Sep 23 '21 at 18:55
  • 1
    This worked for me! I need to make a change to your code, though. Instead of: `include "app.aab"` I had to make this `include "app-${flavor}.aab"` And instead of `rename "app.aab", "${flavor}.aab"` it needed to be `rename "app-${flavor}.aab", "${flavor}.aab"` I made some other adjustments after this to the build directory and filename which worked totally fine. – Nabil Freeman Oct 06 '21 at 17:27
14

Solution from @SaXXuM works great! Task is not necessary for renaming artifact. You can call setProperty() directly in the android {} block. I prefer to have in the file name:

  • app id
  • module name
  • version name
  • version code
  • date
  • build type

This is how I use it in my projects:

build.gradle:

apply from: "../utils.gradle"

android {
    ...
    setProperty("archivesBaseName", getArtifactName(defaultConfig))
}

utils.gradle:

ext.getArtifactName = {
    defaultConfig ->
        def date = new Date().format("yyyyMMdd")
        return defaultConfig.applicationId + "-" + project.name + "-" + defaultConfig.versionName + "-" + defaultConfig.versionCode + "-" + date
}

The result is:

com.example-app-1.2.0-10200000-20191206-release.aab

It works for both - APK and AAB.

Genusatplay
  • 761
  • 1
  • 4
  • 15
petrnohejl
  • 7,581
  • 3
  • 51
  • 63
  • 8
    what if the app has different flavors? – Amrut Bidri Mar 12 '20 at 10:03
  • Is it build type or flavor at the end? – Shrikant Jan 28 '21 at 05:43
  • 1
    This is the most simple way. The other answers are way to complicated. – philk Mar 27 '21 at 19:47
  • The app flavors will be appended to the end of the file name. This is why the property is called "base name", it gives a base to the file name, but the exact file name will be provided according to the flavor. TBH, for a while I was annoyed by not being able to control the file name exactly, but this solution is the most straightforward and compatible with the Android build processing. – racs Apr 27 '23 at 22:49
  • 2
    this is not suitable for app flavors. – Tina Lu Jun 05 '23 at 15:41
7

Now I've wrote kind of a Exec template for cross-platform CLI execution, no matter what the commandLine is. My RenameTask can detect Linux & Windows, as well as release & debug.

Property archivesBaseName needs to be defined in defaultConfig:

android {
    defaultConfig {
        setProperty("archivesBaseName", "SomeApp_" + "1.0.0")
    }
}

RenameTask extends Exec performs the renaming (not to be confused with type: Rename):

import javax.inject.Inject

/**
 * App Bundle RenameTask
 * @author Martin Zeitler
**/
class RenameTask extends Exec {
    private String buildType
    @Inject RenameTask(String value) {this.setBuildType(value)}
    @Input String getBuildType() {return this.buildType}
    void setBuildType(String value) {this.buildType = value}
    @Override
    @TaskAction
    void exec() {
        def baseName = getProject().getProperty('archivesBaseName')
        def basePath = getProject().getProjectDir().getAbsolutePath()
        def bundlePath = "${basePath}/build/outputs/bundle/${this.getBuildType()}"
        def srcFile = "${bundlePath}/${baseName}-${this.getBuildType()}.aab"
        def dstFile = "${bundlePath}/${baseName}.aab"
        def os = org.gradle.internal.os.OperatingSystem.current()
        if (os.isUnix() || os.isLinux() || os.isMacOsX()) {
            commandLine "mv -v ${srcFile} ${dstFile}".split(" ")
        } else if (os.isWindows()) {
            commandLine "ren ${srcFile} ${dstFile}".split(" ")
        } else {
            throw new GradleException("Cannot move AAB with ${os.getName()}.")
        }
        super.exec()
    }
}

And it finalizes two other tasks:

// it defines tasks :renameBundleRelease & :renameBundleDebug
task renameBundleRelease(type: RenameTask, constructorArgs: ['release'])
task renameBundleDebug(type: RenameTask, constructorArgs: ['debug'])

// it sets finalizedBy for :bundleRelease & :bundleDebug
tasks.whenTaskAdded { task ->
    switch (task.name) {
        case 'bundleRelease': task.finalizedBy renameBundleRelease; break
        case   'bundleDebug': task.finalizedBy renameBundleDebug; break
    }
}

The advance is, that it leaves nothing behind and one can move the files wherever one wants.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • I am getting a renamed copy of app.aab (leaving two files behind). How do I delete the app.aab? Based on the timestamps, it appears the app.aap is getting generated after the rename, so deleting it in the same rename task does not work. Any help is much appreciated – Scott B Jun 14 '19 at 17:51
  • 1
    @ScottB one cannot delete with a `Rename` task; this would require a futher `Delete` task, which `dependsOn` that `Copy` task (or the Apache Ant integration to move the file). At least this approach does not generate more tasks than required, as the accepted answer does - and it should work better with flavors, because each of them has their own final task-name. – Martin Zeitler Jul 21 '19 at 18:00
  • @ScottB : I have the same problem. Were you able to get the solution for this ? I tried the dependsOn , but it is not deleting the old file. – user1340801 Aug 25 '19 at 09:22
  • it does not read `dependsOn` and the above `Exec` task is working. – Martin Zeitler Aug 25 '19 at 13:28
  • Nice solution, but how would you do the same on Windows? I tried to put ren or copy instead of "mv", but system doesn't recognize the command! – 3c71 Feb 10 '20 at 09:49
  • @3c71 On Windows 10 this would be `ren` or `rename`, which are both known. – Martin Zeitler Feb 10 '20 at 10:40
  • @MartinZeitler, it doesn't work. you get a "system doesn't recognize the command". As I already pointed out. I've added a proper solution for Windows 10. – 3c71 Feb 11 '20 at 11:03
  • @3c71 On Windows 10 Professional it is known; at least since Toshiba DOS and I don't think so. Added support for multiple OS. And you don't require a `*.bat` for two commands; it's `def command = "COPY ${srcFile} ${dstFile} && RMDIR /Q/S ${bundlePath}"` `commandLine = command.split(" ")`. Also see [What does “&&” do in this batch file?](https://stackoverflow.com/a/28891643/549372) – Martin Zeitler Feb 13 '20 at 13:35
  • @Martin, thanks for the lesson. However it still doesn't work. The problem is not what is known since ages, but what actually works from Android Studio. – 3c71 Feb 14 '20 at 14:34
5

Why no one is using existing gradle tasks for this?

There is a gradle task with the type FinalizeBundleTask and it is called as the last step of bundle generation and it is doing two things:

  • Signing generated AAB package
  • Move and rename AAB package where was requested

All You need to do is just to change the "output" of this task to any that You want. This task contains a property finalBundleFile - full path to the final AAB package.

I'm using it something like that:

    applicationVariants.all {
        outputs.all {
            // AAB file name that You want. Falvor name also can be accessed here.
            val aabPackageName = "$App-v$versionName($versionCode).aab"
            // Get final bundle task name for this variant
            val bundleFinalizeTaskName = StringBuilder("sign").run {
                // Add each flavor dimension for this variant here
                productFlavors.forEach {
                    append(it.name.capitalizeAsciiOnly())
                }
                // Add build type of this variant
                append(buildType.name.capitalizeAsciiOnly())
                append("Bundle")
                toString()
            }
            tasks.named(bundleFinalizeTaskName, FinalizeBundleTask::class.java) {
                val file = finalBundleFile.asFile.get()
                val finalFile = File(file.parentFile, aabPackageName)
                finalBundleFile.set(finalFile)
            }
        }
    }

It works perfectly with any flavors, dimensions, and buildTypes. No any additional tasks, works with any path set for output in Toolbar -> Generate signed Bundle, a unique name can be set for any flavor.

don11995
  • 715
  • 6
  • 18
2

I've found a much better option to auto increment your app versioning and auto renaming when you generate an apk / aab. Solution as below (do remember to create "version.properties" file on your root folder:

android {
     ...
     ...
    Properties versionProps = new Properties()
    def versionPropsFile = file("${project.rootDir}/version.properties")
    versionProps.load(new FileInputStream(versionPropsFile))
    def value = 0
    def runTasks = gradle.startParameter.taskNames
    if ('assemble' in runTasks || 'assembleRelease' in runTasks) {
        value = 1
    }
    def versionMajor = 1
    def versionPatch = versionProps['VERSION_PATCH'].toInteger() + value
    def versionBuild = versionProps['VERSION_BUILD'].toInteger() + 1
    def versionNumber = versionProps['VERSION_NUMBER'].toInteger() + value
    versionProps['VERSION_PATCH'] = versionPatch.toString()
    versionProps['VERSION_BUILD'] = versionBuild.toString()
    versionProps['VERSION_NUMBER'] = versionNumber.toString()
    versionProps.store(versionPropsFile.newWriter(), null)

    defaultConfig {
    applicationId "com.your.applicationname"
    versionCode versionNumber
    versionName "${versionMajor}.${versionPatch}.${versionBuild}(${versionNumber})"
    archivesBaseName = versionName
    minSdkVersion 26
    targetSdkVersion 29
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    signingConfig signingConfigs.release
    setProperty("archivesBaseName","${applicationId}-v${versionName}")

    ...
}

Credits to this website and this post

kabayaba
  • 195
  • 1
  • 13
0

Based on Martin Zeitler's answer I did this on Windows:

Please note that on my setup, .aab files are created in release folder and it deletes everything else in that folder as per this bug report.

In my app's module gradle:

apply from: "../utils.gradle"

...

tasks.whenTaskAdded { task ->
    switch (task.name) {
        case 'bundleRelease':
            task.finalizedBy renameBundle
            break
    }
}

And in utils.gradle:

task renameBundle (type: Exec) {
    def baseName = getProperty('archivesBaseName')

    def stdout = new ByteArrayOutputStream()
    def stderr = new ByteArrayOutputStream()

    commandLine "copy.bat", rootProject.getProjectDir().getAbsolutePath() + "\\release\\${baseName}-release.aab", "<MY_AAB_PATH>\\${baseName}.aab", "D:\\Android\\studio\\release"
    workingDir = rootProject.getProjectDir().getAbsolutePath()
    ignoreExitValue true
    standardOutput stdout
    errorOutput stderr

    doLast {
        if (execResult.getExitValue() == 0) {
            println ":${project.name}:${name} > ${stdout.toString()}"
        } else {
            println ":${project.name}:${name} > ${stderr.toString()}"
        }
    }
}

The copy.bat is created in project's folder and contains this:

COPY %1 %2
RMDIR /Q/S %3

Be careful with 3rd argument to make sure you don't use a folder that's important to you.

EDIT: Why a .BAT for 2 commands you might ask. If you try commandLine "copy", ... on Windows it results in "system does not recognize the command copy". Put anything, like COPY, REN, RENAME, etc, won't work.

3c71
  • 4,313
  • 31
  • 43