Generate APK with declared name in Gradle without any suffixes - android

I know that it is possible to specify APK name by using following command in Gradle build file:
setProperty("archivesBaseName", "something")
There are lots of questions here that deal with this. But doing this also results in APK that has flavor and build variant appended to the name. So using above command results in APK that's named something-dev-debug.apk.
Is there a way to completely specify file name without appending any suffixes. I already have appId in my flavors like this:
//...
productFlavors {
dev {
applicationId "com.myapp.dev"
setProperty("archivesBaseName", applicationId)
}
//...
So ideally I would like to produce APK named com.myapp.dev.apk without any extra suffixes or version numbers. Is this possible?

It seems that this shouldn't be done with setPropery. Rather you can define handler that changes output file name as final step by adding following code to the end of android { } section in gradle file:
applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
def appId = variant.applicationId
def fileName = appId +".apk"
output.outputFile = new File(file.parent, fileName)
}
}

Related

Android gradle build config - Rename generated APKs

I need to save each APK with a special name whenever I build one.
This is the desired name format: "buildType appName_version_Date_Time.apk"
Example: "debug myAppName_v1.0_20161009_0854.apk"
Example: "release myAppName_v1.0_20161009_0854.apk"
For this purpose I added this gradle script:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def formattedDate = new Date().format('yyyyMMdd_HHmm')
println(output.outputFile)
def newName = variant.name + " myAppName_v" + defaultConfig.versionName + "_" + formattedDate + ".apk"
output.outputFile = new File(output.outputFile.parent, newName)
}
}
}
This script works, but android studio couldn't find the renamed APK. I also tried clean/rebuild project but it ocures again.
Android stuio error:
The APK file path\to\project\app\build\outputs\apk\debug myAppName_v1.0_[20161009_0854].apk does not exist on disk.
Error while Installing APK
When I check "build\outputs\apk" directory i see that an APK has been built and its name is "myAppName_v1.0_[20161009_0856].apk".
What's wrong here??
Instead of including the current time (with minutes, no less) in the APK filename, consider the following approaches:
Take the timestamp of the latest version control commit (that stays stable)
Instead of changing the apk filename, copy it from it's non-timestamped name - that Android Studio will easily find - to a timestamped apk.
Here's an example to try (haven't run it but should give the general idea). Choose either Files.copy or the copy task, not both.
applicationVariants.all { variant ->
variant.outputs.each { output ->
def copyApkTask = tasks.create(name: "copy" + variant.name + "Apk") {
def newName = ... // can include the timestamp with minutes
println(newName)
// Using java.nio.Files.copy
def targetPath = new File(output.outputFile.parent, newName).toPath()
Files.copy(output.outputFile.toPath(), targetPath)
// Using gradle's Copy logic (clunky for single-file copy and rename)
copy {
from output.outputFile
into output.outputFile.parent
rename { String fileName ->
newName
}
}
}
copyApkTask.mustRunAfter variant.assemble
}
}
you are telling gradle to include the minute in the name of your apk.
then it builds it with the name you want in the n minute, and when it wants to refer to it in the n+1 minute it cant find the file. so it encounters this error.
you better use the commit hash or the date in your apk name. (not including small units like minute or second or even hour)

Android Gradle Read App Name from strings.xml

I am trying to rename my APK files for each build variant to include the application name, versionName, versionCode and build number when present. So far I have everything working except the application name.
I want to use the same value that the AndroidManifest.xml file uses for android:label. This comes from a string resource #string/app_name. I have seen the ability to replace the resource values by using:
resValue "string", "app_name", "Some new value"
But I would just like to read this value and use it to name my APK file.
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
renameApk(variant, output)
}
}
def renameApk(variant, output) {
def apkPath = output.outputFile.parent
def baseName = project.archivesBaseName
baseName += "-${variant.buildType.name}"
// add version name and version code
baseName += "-v${variant.mergedFlavor.versionName}-${variant.mergedFlavor.versionCode}"
// if built on jenkins ci, add jenkins build number:
def buildNumber = System.getenv('BUILD_NUMBER')
if (buildNumber && buildNumber.size() > 0) {
baseName += "-b${buildNumber}"
}
// if the variant will not be zipAligned, specify that
if (!output.zipAlign) {
baseName += '-unaligned'
}
// set the output file
output.outputFile = new File(apkPath, "${baseName}.apk");
}
I don't see any method in Android Plugin docs for accessing resources, so here is the code you can use to find your app's name by searching resources:
def getAppName() {
def stringsFile = android.sourceSets.main.res.sourceFiles.find { it.name.equals 'strings.xml' }
return new XmlParser().parse(stringsFile).string.find { it.#name.equals 'app_name' }.text()
}
BUT I completely agree with #Samuil Yanovski in that it is not worth it - better hardcode a string. I don't think it will slow down building process, but it is just unnecessary.
I don't think this can be done easily. Resource resolution is done on the mobile device to accommodate for things like screen orientation, localization and so on. The Gradle build system has no way of knowing which locale to use for example. If you insist on getting the value from the resources, you can open the specific strings.xml file you'd like to use, parse the XML and get the value yourself. In my opinion this is a huge overkill and would be pretty slow and ugly.
App name is not changed often, so I would be comfortable with having it hardcoded (especially since the apk file name is not visible to the end user, so even if mistakes happen, the impact would be minimal). If you are working on a white label application and have to support dynamic app name, extracting the value to the gradle.properties file (or some other type of configuration file, you are using) should be a better option rather than using the app's resources.
I have create method using #Yaroslav's answer (https://stackoverflow.com/a/37432654/6711554).
def getApplicationName() {
try {
def stringsFile = file("./src/main/res/values/string.xml")
return new XmlParser().parse(stringsFile).string.find { it.#name.equals 'your_app_name' }.text()
}catch(e){
println(e)
return "Default App Name"
}
}
You can read any string in your gradle from your any resource file.

Renaming gradle apk output file, but there are 2 apks

I am trying to name my application's APK file based on the git branch, which is working by passing a parameter in with jenkins. However, I end up with two APKs, and this is undesirable because one of the Gradle tasks I have uploads the APK to a distributed list. The whole point of renaming this APK is so that people who receive it know exactly what branch they are seeing.
buildTypes {
...
feature {
signingConfig signingConfigs.debug
applicationVariants.all { variant ->
appendVersionNameVersionCode(variant, defaultConfig)
}
}
...
}
...
def appendVersionNameVersionCode(variant, defaultConfig) {
//check if staging variant
if(variant.name == android.buildTypes.feature.name){
def branch = hasProperty('branch') ? branch.replaceAll('origin/', '') : "UNKNOWN"
def file = variant.packageApplication.outputFile
def fileName = "myapp-FEATURE-" + branch + ".apk"
variant.packageApplication.outputFile = new File(file.parent, fileName)
}
}
Then the gradle command is invoked:
gradle assembleFeature --project-dir=/Path/To/myapp/ -Pbranch=origin/development
The two files that are generated from that are:
app-feature.apk
myapp-FEATURE-development.apk
File 1 is not desired, and I would ultimately want to rename that file, instead of generating a new file which is File 2.
Any thoughts?
Turns out, gradle always packages up 2 apks. One that is aligned, and one that isn't.
In order to rename the aligned apk file, I had to add this:
if (variant.zipAlign) {
newName = "myapp-FEATURE-" + branch + "-ALIGNED.apk"
variant.outputFile = new File(file.parentFile, newName);
}

Android Studio Gradle assembleRelease file name

Is it possible to specify the filename of the generated *.apk through gradle? So that I automatically could get MyApp-1.0.0.apk instead of the default app-release.apk.
You can do this by adding the following line to your build.gradle file inside the android{...} part:
android.applicationVariants.all { variant ->
variant.outputFile = file("$project.buildDir/${defaultConfig.versionName}-${defaultConfig.versionCode}.apk")
}
Notes:
Everything inside the file(...) is arbitrary, of course
This code will output the *.apk to your project/module/build directory, no matter what path you've specified when Building the Signed APK.
The output-file will take the Name and Version-Code from your build.gradle file.
Android Studio may complain that it "cannot resolve symbol applicationVariants/defaultConfig" - ignore it. ;)
Got it working! Used this answer
android {
applicationVariants.all { variant ->
variant.outputs.each { output ->
output.outputFile = new File(
output.outputFile.parent,
output.outputFile.name.replace(".apk", "-${variant.versionName}.apk"))
}
}
}

Setting app label based on buildvariant

I have an Android with multiple productFlavors. I'd like the android:label for the debug build of, for example flavor1, to say Flavor1 Debug and the release build to say Flavor1.
Without productFlavors this is relatively simple, just create a different string resource in the debug and release source folders but with productFlavors, I don't want to be creating flavor1Debug, flavor2Debug, etc. folders for each flavor with just one string resource in there.
Is there a way to do this ? I'm guessing it requires merging resources somehow but am not sure how.
For extra points, it would be awesome if I could add an overlay to the app icon i.e. merge two images but I realize that might be taking it too far.
I worked out a solution for this. Basically, you use the following method that copies read the build file, modifies the property value, then rewrites the files.
def debugAppRename(variant, labelResource) {
def flavor = variant.productFlavors.get(0)
def buildtype = variant.buildType
// Append buildType name to app label
if(buildtype.debuggable) {
variant.mergeResources << {
def valuesFile = "$buildDir/res/all/${flavor.name}/${buildtype.name}/values/values.xml"
def values = (new XmlParser()).parse(valuesFile)
values.string.each { m->
if (m.#name == labelResource) {
m.value = m.text() + " " + buildtype.name.capitalize()
new XmlNodePrinter(new PrintWriter(new FileWriter(valuesFile)))
.print(values)
}
}
}
}
}
The way to use it is:
applicationVariants.all { variant ->
debugAppRename(variant, 'app_name') // where `app_name` is the string resource you use for the `app:label` property in your AndroidManifest.xml
}
The resulting app will have the buildType name appended to it if it is a debuggable buildType, for e.g. My App Debug, My App Staging, etc. The release build remains unaffected.
I also put it up on a gist.
You can apply it in your build.gradle using the apply from: directive with the raw gist URL or copy the above to your code (personally I find build scripts become too large so I prefer applying from).

Categories

Resources