I'm building a gradle plugin that adds a new task for every application variant. This new task needs the package name of the application variant.
This is my current code, which stopped working with the most recent version of the android gradle plugin:
private String getPackageName(ApplicationVariant variant) {
// TODO: There's probably a better way to get the package name of the variant being tested.
String packageName = variant.generateBuildConfig.packageName
if (variant.processManifest.packageNameOverride != null) {
packageName = variant.processManifest.packageNameOverride
}
packageName
}
This has stopped working in the most recent version of the android plugin because of changes to the build config processing. It seemed like a hack before anyway, so I'm not surprised it stopped working. Is there a canonical way to fetch the package name?
I use this:
// Return the packageName for the given buildVariant
def getPackageName(variant) {
def suffix = variant.buildType.packageNameSuffix
def packageName = variant.productFlavors.get(0).packageName
if (suffix != null && !suffix.isEmpty() && suffix != "null") {
packageName += suffix
}
return packageName
}
The only way I found was to define packageName in the project's build.gradle and then from the plugin do project.android.defaultConfig.packageName.
If the project has flavors that define their own package then the solution stated by stealthcopter would work.
However a plugin is meant to work with any project regarless whether packageName is defined or not in build.gradle so these solutions are far from ideal.
Let me know if you find something better.
This will handle nullable applicationIdSuffix for you:
android.applicationVariants.all { variant ->
def applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join()
println applicationId
}
Related
I'm trying to follow https://developer.android.com/studio/build/maven-publish-plugin to publish library (aar) module and application (apk) module to Maven repository (maven-publish plugin). I'm using Kotlin Gradle kts instead of Grovy.
Sample publish in Grovy (from link above)
publishing {
publications {
paidRelease(MavenPublication) {
// The following applies a component to this publication
// which results in publishing an app bundle.
from components.paidRelease_aab
groupId = 'com.example.MyApp'
artifactId = 'paid-release-aab'
version = '1.0'
}
paidDebug(MavenPublication) {
// The following applies a component to this publication
// which results in publishing APKs in a ZIP file.
from components.paidDebug_apk
groupId = 'com.example.MyApp'
artifactId = 'paid-debug-apks'
version = '1.0'
}
}
}
My kotlin dls publishing code for app module
publications {
create<MavenPublication>("mastercardMavenApk") {
groupId = ProjectInfo.groupId
artifactId = ProjectInfo.artifactId
version = ProjectInfo.nexusVersion + if (useReleaseRepo) "" else "-SNAPSHOT"
from(components["mastercardDebug_apk"])
}
}
where mastercardDebug is one of my Active Build Variant
I always have below error:
SoftwareComponentInternal with name 'mastercardDebug_apk' not found
What should be the correct way to use maven-publish plugin for Android project (support both aar and apk module)?
just use print(components.names) before from(components["mastercardDebug_apk"])
to verify that the component exsists. maybe its just a typo
Problem: setting applicationId depending on flavor.
More problem: Two apps are already on the store, both having a different style of applicationId.
com.name.dimension1.dimension2
com.name.dimension1dimension2 (without dot)
In our Android app we need to introduce new flavors/dimensions.
Dimensions:
flavorDimensions "company", "app", "server"
Seeing that, this is why we cannot use applicationIdSuffix in build.gradle because it is automatically adding . (dot) before suffix.
We already have method to decide which versionCode should be done for every flavor (thanks to that answer on Stack)
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find())
return matcher.group(1).toLowerCase()
else {
println "NO MATCH FOUND"
return ""
}
Even more problem: Same method which helps us with setting version code, cannot help with applicationId.
def getFlavorApplicationId() {
def flavor = getCurrentFlavor()
if (flavor.contains("company1") && flavor.contains("app1")) {
return ext.company1app1AppId
} else if (flavor.contains("company2") && flavor.contains("app1")) {
return ext.company2app1AppId
} else if (flavor.contains("company2") && flavor.contains("app2")) {
return ext.company2app2AppId
}
return "nothing"
}
When app is built/synchronized - everything is working properly (file BuildConfig and also generated apk is having correct applicationId).
The problem is occurring when we are trying to Run the app with applicationId is depending on flavor.
Error while executing: am start -n "**non.of.those**/com.rsqtechnologies.rsqphysio.splash.SplashActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=non.of.those/com.rsqtechnologies.rsqphysio.splash.SplashActivity }
Error type 3
Error: Activity class {**non.of.those**/com.rsqtechnologies.rsqphysio.splash.SplashActivity} does not exist.
Error while Launching activity
If I understand this well, Android Studio is not taking applicationId to run the app from BuildConfig or even generated .apk. It is trying to generate it while launching app (when it cannot gather information about flavor from gradle task - def getCurrentFlavor())
when I am running the app by myself in Terminal with the same command but correct appId - everything works fine.
Things which I have already tried also:
Solution from this Stack answer
Tried to find a way in Android Studio to edit the configuration so it would take proper appId (failed)
Does anyone have some advice? Pretty complex problem, I can share more details if anyone interested.
When you are using groovy you can change the applicationId for all your merged flavors:
android.applicationVariants.all { variant ->
def isApp1 = variant.name.contains('app1')
def isApp2 = variant.name.contains('app2')
def idAppendix = ""
if (isApp1) idAppendix = ".withDotForApp1"
if (isApp2) idAppendix = "noDotForApp2"
mergedFlavor.setApplicationId(mergedFlavor.applicationId + idAppendix)
}
Note that this won't work if you plan to use kotlinscript since the applicationid of the merged flavor is a val and doesnt offer a setter.
This time I have this problem, I am trying to get the current flavor in a gradle script. I have tried the answers given here How to get current flavor in gradle
without luck.
I haven seen in some answers that they use
// Get all flavors
android.productFlavors.all { flavor ->
if (flavor.name.equals("flavorName")) {
// do what you like
}
// ...
}
But also I didn't have any luck with that because i get the following error: > Could not get unknown property 'android' for task
So I don't know how to get the current flavor, any help will be very appreciated thanks!!!
EDIT: What I need to do is to execute a piece of code that is diferent for each flavor, my current idea is to know the selected build variant to do this in a task, but if there is any othe way to do this would be perfect.
I already posted a working solution here, that is:
The following function returns exactly the current flavor name:
def getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if( tskReqStr.contains( "assemble" ) )
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher( tskReqStr )
if( matcher.find() )
return matcher.group(1).toLowerCase()
else
{
println "NO MATCH FOUND"
return "";
}
}
You need also
import java.util.regex.Matcher
import java.util.regex.Pattern
at the beginning or your script.
In Android Studio this works by compiling with "Make Project" or "Debug App" button.
You can get this error if your use it out of "android" closure at app level gradle script. Make sure that you use it inside
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.
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).