Android: add date/time to gradle output apk filename - android

How to add date/time stamp to Gradle Android output file name?
Should be like project_v0.5.0_201503110212_public.apk
Already looked at
how append date build to versionNameSuffix on gradle
How to pass arguments from command line to gradle

I'm assuming that you want it in the format you specified, so here's one possible solution.
In your gradle file you can define a new function to get the date time string like you desire:
import java.text.DateFormat
import java.text.SimpleDateFormat
def getDateTime() {
DateFormat df = new SimpleDateFormat("yyyyMMddHHmm");
return df.format(new Date());
}
Then for all variants you can simply run this:
android {
//...
buildTypes {
//...
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + getDateTime() + ".apk"))
}
}
}
}
Note that this doesn't really output the apk name like you posted, but I guess it's enough to help you out.

This code working for me.
applicationVariants.all { variant ->
variant.outputs.each { output ->
def project = "Your App Name"
def SEP = "_"
def flavor = variant.productFlavors[0].name
def buildType = variant.variantData.variantConfiguration.buildType.name
def version = variant.versionName
def date = new Date();
def formattedDate = date.format('ddMMyy_HHmm')
def newApkName = project + SEP + flavor + SEP + buildType + SEP + version + SEP + formattedDate + ".apk"
output.outputFile = new File(output.outputFile.parent, newApkName)
}
}

I also add a formatted date to my build. In first place I used some kind of "now" with new Date(), but this leads to trouble when starting the build with Android Studio, like in one of the comments above. I decided to use the timestamp of the latest commit. I found some inspiration here: https://jdpgrailsdev.github.io/blog/2014/10/14/spring_boot_gradle_git_info.html
Adding the timestamp is handled like below:
def getLatestCommitTimeStamp() {
def revision = 'git rev-list --max-count 1 --timestamp HEAD'.execute().text.trim()
def gitCommitMillis = java.util.concurrent.TimeUnit.SECONDS.toMillis(revision.split(' ').first() as long)
return new Date(gitCommitMillis).format("_HH.mm.ss_dd-MM-yyyy", TimeZone.getTimeZone('Europe/Berlin'))
}
My renaming part looks like this:
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def lastCommitFormattedDate = getLatestCommitTimeStamp()
variant.outputs.each { output ->
def alignedOutputFile = output.outputFile
def unalignedOutputFile = output.packageApplication.outputFile
// Customise APK filenames (to include build version)
if (variant.buildType.zipAlignEnabled) {
// normal APK
output.outputFile = new File(alignedOutputFile.parent, alignedOutputFile.name.replace(".apk", "-v" + defaultConfig.versionName + "-" + variant.buildType.name.toUpperCase() + "-${gitSha}" + lastCommitFormattedDate + ".apk").replace("-" + variant.buildType.name, "").replace(project.name, "otherName"))
}
// 'unaligned' APK
output.packageApplication.outputFile = new File(unalignedOutputFile.parent, unalignedOutputFile.name.replace(".apk", "-v" + defaultConfig.versionName + "-" + variant.buildType.name.toUpperCase() + "-${gitSha}" + lastCommitFormattedDate + ".apk").replace("-" + variant.buildType.name, "").replace(project.name, "otherName"))
}
}

An alternative solution is to set $dateTime property in defaultConfig as shown below:
defaultConfig {
setProperty("archivesBaseName", "Appname-$dateTime-v$versionName")
}

This is mine hope to help you
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
output.outputFile = new File(
(String) file.parent,
(String) file.name.replace(
file.name,
// alter this string to change output file name
"APigLive_Android_" + variant.name + "_" + variant.versionName + "_" + releaseTime() + ".apk"
)
)
}
}
def releaseTime(){
return new Date().format("MM:dd:HH:mm", TimeZone.getTimeZone("GMT"))
}

You can just add the code below inside the defaultConfig section located in android section.
setProperty("archivesBaseName", "yourData-$versionName " + (new Date().format("HH-mm-ss")))
Inspired by enter link description here

Related

Could not get unknown property 'apkNames' in android gradle plugin 4.1.0

I am using this code in build.gradle:
android {
applicationVariants.all { variant ->
variant.packageApplicationProvider.configure { packageApplicationTask ->
doLast {
packageApplicationTask.apkNames.each { apkName ->
def apkDir = "./build/outputs/apk/${flavorName}/${buildType.name}"
def apkDestName = apkName.replace("app-", "stickerly-android-" + variant.versionName + "-").replace(".apk", "-" + getDate() + ".apk")
println "#####Rename ${variant.name} Apk File"
copy {
from("$apkDir/$apkName").into(apkDir).rename { String fileName -> apkDestName }
}
println "#####Copy mapping File"
def mappingDir = "./build/outputs/mapping/${flavorName}${buildType.name.capitalize()}"
copy {
from("$mappingDir/mapping.txt").into(mappingDir).rename {
"mapping-stickerly-${variant.versionName}.txt"
}
}
}
}
}
}
}
With this code I rename apk file and copy mapping file. I worked in android gradle plugin 4.0, but it does not work in 4.1 with this message
Where:
Script '/Users/snow/workspace/stickerly-android/app/build-android-extra.gradle' line: 5
What went wrong:
Execution failed for task ':app:packageExternalArm8Debug'.
Could not get unknown property 'apkNames' for task ':app:packageExternalArm8Debug' of type com.android.build.gradle.tasks.PackageApplication.
I think API has changed but I can not find any documents. Someone can help me?
Thanks.
property apkNames is removed in AGP 4.1
you can try this
gradle.taskGraph.afterTask { Task task, TaskState state ->
if (!state.failure
&& task.project.name == project.name
&& task.name.startsWith("package")
&& !task.name.endsWith("UnitTest")) {
def outputDir = task.outputDirectory.getAsFile().get()
task.variantOutputs.getOrNull()?.each { variant ->
println "" + outputDir + "/" + variant.outputFileName.get()
}
}
}
add this at the end of your build.gradle file.
change println to whatever logic you want.
by the way, if you want to check the properties you may use, just add gradle plugin as a dependency of your project, click Sync in Android Stuido, then you can find it in External Librarys (key map: Command + 1, and switch to project view).
like this
dependencies {
implementation 'com.android.tools.build:gradle:4.1.0'
}
and these properties and tasks are intentionally invisible in lib com.android.tools.build:gradle-api, modifications in future releases are expected.

Gradle copy and rename in loop

I'm working with Android Studio 3.
For each flavor, I want to copy mapping.txt and rename it.
My Gradle task :
task deployApks(type: Copy) {
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.outputs.all {
def flavor = variant.productFlavors.name.get(0)
def dirApk = System.getProperty("user.dir") + '/app/build/' + flavor + '/release/'
def dirMapping = System.getProperty("user.dir") + '/app/build/outputs/mapping/' + flavor + '/release/'
//copy apk and mapping.txt
from dirApk, dirMapping
include '*-release.apk', 'mapping.txt'
into dirDeploy
//rename mapping.txt
from dirDeploy
include 'mapping.txt'
into dirDeploy
rename 'mapping.txt', 'mapping-' + flavor + '.txt'
println("Rename mapping.txt tomapping-" + flavor + ".txt")
}
}
}
}
What I want in deploy directory :
flavor1-release.apk
mapping-flavor1.txt
flavor2-release.apk
mapping-flavor2.txt
What I get :
flavor1-release.apk
mapping-flavor1.txt
flavor2-release.apk
Is gradle copy asynchronous.?
It looks like if renaming is done after all copies.
You may not know but gradle build consists of 3 phases:
initialisation
configuration
execution
Task (including Copy you used) actions (a task is a collection of actions run in order) are configured in the second phase. Eve if you put loop in task's body the last iteration will win. The easiest way is to change your task to the following (copying manually):
task deployApks {
doLast {
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
variant.outputs.all {
def flavor = variant.productFlavors.name.get(0)
def dirApk = System.getProperty("user.dir") + '/app/build/' + flavor + '/release/'
def dirMapping = System.getProperty("user.dir") + '/app/build/outputs/mapping/' + flavor + '/release/'
//copy apk and mapping.txt
copy {
from dirApk, dirMapping
include '*-release.apk', 'mapping.txt'
into dirDeploy
rename 'mapping.txt', 'mapping-' + flavor + '.txt'
}
}
}
}
}
}
If that solves the problem - (you don't need to task caching) you can work with. Otherwise you need to configure Copy task appropriately or even write a custom task.
I think the key is variant.assemble.doLast. I'm making first all apk files and when finish run doLast task copying and renaming mapping.txt files.
Gradle 4 (compatible)
// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi':3, 'armeabi-v7a':4, 'arm64-v8a':5, 'mips':6, 'x86':7, 'x86_64':8].withDefault {0}
import com.android.build.OutputFile
android.applicationVariants.all { variant ->
def customName = ""
if (project.hasProperty('projectName')) {
customName = projectName
} else {
customName = project.name
}
def flavorName = variant.productFlavors[0].name
def buildType = variant.variantData.variantConfiguration.buildType.name
def abiVersionCode = ""
def abiName = ""
def fileName = ""
def mappingDir = "${rootDir}/build/outputs/mapping/${flavorName}/${buildType}"
variant.outputs.all { output ->
abiVersionCode = project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))
if (abiVersionCode != null) {
output.versionCodeOverride = abiVersionCode * 1000 + variant.versionCode
}
abiName = output.getFilter(OutputFile.ABI)
if (abiName == null) {
abiName = "universal"
output.versionCodeOverride = 1 * 1000 + variant.versionCode
}
fileName = customName + "_" + variant.versionName + "-" + flavorName + "-" + abiName + "-" + buildType + "-" + output.versionCode
outputFileName = new File("${fileName}.apk")
}
variant.assemble.doLast {
variant.outputs.all { output ->
if (buildType == "release") {
def mappingFile = "${mappingDir}/mapping.txt"
def newMappingName = "${fileName}-mapping.txt"
delete "${output.outputFile.parent}/${newMappingName}"
copy {
from "${mappingFile}"
into "${rootDir}"
rename { String mappingName ->
"${output.outputFile.parent}/${newMappingName}"
}
}
}
delete "${output.outputFile.parent}/output.json"
}
}
}

Android gradle change resources on merge

I have a json config file and I put it in res/raw folder. previously I have a gradle script that updates the file contents when it was necessary.
def fetchMeta(buildVariant) {
def flavor = buildVariant.productFlavors.get(0).name
def buildType = buildVariant.buildType.name
def middleMetaFolder = "${buildDir}/intermediates/meta/${flavor}"
def pathToMiddleMeta = "${middleMetaFolder}/latest.json"
def rawFolder = "${buildDir}/intermediates/res/merged/${buildVariant.dirName}/raw/"
def f = new File(pathToMiddleMeta)
boolean doDownload = (!f.exists()) || (f.lastModified() < (System.currentTimeMillis() - 86400000))
// Force on production release
if (doDownload || (flavor == "production" && buildType == "release")) {
new File(middleMetaFolder).mkdirs()
def serverAddress = "https://example.com"
String srcUrl = serverAddress + "/latest.json"
println "Downloading Meta from: " + srcUrl + " to " + pathToMiddleMeta
new URL(srcUrl).withInputStream { i -> f.withOutputStream { it << i } }
} else {
println "Skipping Meta as it exists here: " + pathToMiddleMeta
}
copy {
from pathToMiddleMeta
into rawFolder
}
}
android.applicationVariants.all { variant ->
variant.mergeResources.doLast {
fetchMeta(variant)
}
}
But as of android gradle plugin 3.0 merge strategy changed and files are with flat extension. How can I update the contents of my file after these changes?
had the same issue, which comes with the update from aapt to aapt2
issue is already assigned at google issue-tracker
https://issuetracker.google.com/issues/65220623
as a workaround right now you can set android.enableAapt2=false in your gradle.properties

Gradle, task to copy mapping file

It's necessary to have mapping.txt file to check crashes come from your app (because of ProGuard), in many cases developers forget to copy mapping file and back it up and after next release it will be changed and useless to check previous version bugs.
how to copy mapping file after release and copy version as suffix to it's name in particular path using gradle task automatically?
This is the snippet I use. It does depend on having a productFlavor defined but that is only to help name the file and allow the same snippet to be reused in multiple projects without modification but that dependency could be refactored out if you wanted a different filename format.
As it stands, the apk and the mapping file (if required) will be copied to the defined basePath in the format:
FilePath\appname\appname buildtype versionname (versioncode)
e.g
A:\Common\Apk\MyAppName\MyAppName release 1.0 (1).apk
and
A:\Common\Apk\MyAppName\MyAppName release 1.0 (1).mapping
Amend as you see fit.
android {
productFlavors {
MyAppName {
}
}
//region [ Copy APK and Proguard mapping file to archive location ]
def basePath = "A:\\Common\\Apk\\"
applicationVariants.all { variant ->
variant.outputs.each { output ->
// Ensure the output folder exists
def outputPathName = basePath + variant.productFlavors[0].name
def outputFolder = new File(outputPathName)
if (!outputFolder.exists()) {
outputFolder.mkdirs()
}
// set the base filename
def newName = variant.productFlavors[0].name + " " + variant.buildType.name + " " + defaultConfig.versionName + " (" + defaultConfig.versionCode + ")"
// The location that the mapping file will be copied to
def mappingPath = outputPathName + "\\" + newName + ".mapping"
// delete any existing mapping file
if (file(mappingPath).exists()) {
delete mappingPath
}
// Copy the mapping file if Proguard is turned on for this build
if (variant.getBuildType().isMinifyEnabled()) {
variant.assemble.doLast {
copy {
from variant.mappingFile
into output.outputFile.parent
rename { String fileName ->
newName + ".mapping"
}
}
}
}
// Set the filename and path that the apk will be created at
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
def path = outputPathName + "\\" + newName + ".apk"
if (file(path).exists()) {
delete path
}
output.outputFile = new File(path)
}
}
}
//endregion
}

Access a path from .bashrc in gradle.build on a macbook pro

I have a set a path in my .bashrc which I wanna have access to from my build.gradle file.
I'm using the commandLine method in gradle, but I can't seems to get it working.
My .bashrc:
APK_PATH="echo /Users/CPE/APK"
export APK_PATH
Which give me this result in a terminal:
$APK_PATH
/Users/CPE/APK
In my gradle.build file I have the following code:
def getOutputPath = { ->
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'echo', '$APK_PATH'
standardOutput = stdout
}
return stdout.toString().trim()
}
applicationVariants.all { variant ->
def newName;
def versionNumber = variant.versionName;
def appName = variant.name.replace("Release","");
def date = getDate();
if (variant.buildType.name == "release") {
newName = appName + "_" + date + ".apk";
releaseDir = getOutputPath() + "/" + appName;
} else {
newName = variant.name;
}
variant.outputFile = new File(releaseDir, newName);
}
When i'm trying to make a release build I get the following error:
Unable to open '$APK_PATH/ostran/ostran_20141209.apk' as zip archive
Instead of using the .bashrc file you can use your local gradle.properties which I found WAAAY easier!
You can place a gradle.properties file in the Gradle user home directory. The properties set in a gradle.properties file can be accessed via the project object. The properties file in the user's home directory has precedence over property files in the project directories.
If this property does not exist, an exception will be thrown and the build will fail. If your build script relies on optional properties the user might set, perhaps in a gradle.properties file, you need to check for existence before you access them. You can do this by using the method hasProperty('propertyName') which returns true or false.
my flavors.gradle
applicationVariants.all { variant ->
if (project.hasProperty('APK_PATH')) {
def appName = variant.name.replace("Release", "");
def releaseDir = variant.outputFile.parent
def newName = variant.name;
if (variant.buildType.name == "release") {
newName = appName + "_" + getDate() + ".apk";
releaseDir = APK_PATH + appName;
}
variant.outputFile = new File(releaseDir, newName);
}
}

Categories

Resources