Android project How to use/merge multiple AndroidManifest.xml - android

I want to split my project to multiple sourceSets, so I want use multiple AndroidManifest.xml to declare current sourceSets's component,util build apk merge the main AndroidManifest.xml, but there only support one main AndroidManifest.xml setup, so how can I do it?
Why I want to use multiple sourceSets? Because if using multiple modules it compile so slow.
This is my project settings
def moduleDirs = project.projectDir.listFiles()
.findAll {
def name = it.name
// filter all inner's module
it.isDirectory() && msExtension.modulePrefix.any { name.startsWith(it) }
}
Set<File> manifestSet = new HashSet<>()
Set<String> modules = new HashSet<>()
def sourceSetConf = { AndroidSourceSet sourceSet ->
moduleDirs.each {
def moduleName = it.name
def dir = sourceSet.name
sourceSet.assets.srcDirs "${moduleName}/src/${dir}/assets"
sourceSet.java.srcDirs "${moduleName}/src/${dir}/java"
sourceSet.res.srcDirs "${moduleName}/src/${dir}/res"
sourceSet.aidl.srcDirs "${moduleName}/src/${dir}/aidl"
// each AndroidManifest.xml
def manifestFile = project.file("${moduleName}/AndroidManifest.xml")
if (manifestFile != null && manifestFile.exists()) {
manifestSet.add(manifestFile)
}
modules.add(moduleName)
}
}
// for default main sources
sourceSetConf(android.sourceSets.main)
// for buildType sources
android.buildTypes.each {
def buildType = it.name
android.sourceSets.getByName(buildType) {
sourceSetConf(it)
}
}
// for flavor sources
android.productFlavors.each {
def flavor = it.name
android.sourceSets.getByName(flavor) {
sourceSetConf(it)
}
}
I see some code from gradle what multiple modules merge, but still no idea how to
ManifestMerger2
ProcessManifest

Use multiple modules via Android Studio. Create dependencies between modules. To make ModuleA depend on ModuleB, put the following inside build.gradle of ModuleA:
dependencies {
// ...
compile project(':ModuleB')
// ...
}
This should merge AndroiManifest.xml from both modules when buiding ModuleA

I just use ManifestMerger2 to do it.
private static void merge(File reportFile, File mainManifest, File... libraryManifests) {
if (libraryManifests == null || libraryManifests.length == 0) {
return
}
ILogger logger = new StdLogger(Level.VERBOSE)
Invoker manifestMergerInvoker = ManifestMerger2.newMerger(mainManifest, logger, MergeType.APPLICATION)
manifestMergerInvoker.setMergeReportFile(reportFile)
manifestMergerInvoker.addLibraryManifests(libraryManifests)
println "start merge..."
MergingReport mergingReport = manifestMergerInvoker.merge()
println "end merge..."
if (MergingReport.Result.SUCCESS) {
XmlDocument xmlDocument = mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED)
try {
String annotatedDocument = mergingReport.getActions().blame(xmlDocument)
logger.verbose(annotatedDocument)
} catch (Exception e) {
logger.error(e, "cannot print resulting xml")
}
save(xmlDocument, mainManifest)
}
}
private static void save(XmlDocument xmlDocument, File out) {
try {
Files.write(xmlDocument.prettyPrint(), out, Charsets.UTF_8)
} catch (IOException e) {
throw new RuntimeException(e)
}
}

Related

EasyLauncherConfig with name 'debug' not found

I'm using easylaucher library Easylaucher library to add a ribbon on the app icon. I'm using kotlin DSL but when I apply the easy laucher configurations it throws EasyLauncherConfig with name 'debug' not found.
Here is my configuration
plugins {
id("kotlin-android")
id(BuildPlugins.androidApplication)
id("com.starter.easylauncher") version "5.1.2"
}
android {
//...
}
dependencies {
//...
}
easylauncher {
buildTypes{
getByName("debug") {
filters(
customRibbon(label = "Debug", ribbonColor = "#FF0000")
)
}
getByName("release") {
filters(
customRibbon(label = "BETA", ribbonColor = "#F18357")
)
}
}
}
Anyone with a good solution to this?
I found the solution. Instead of using getByName(...) I used register(...)
easylauncher {
buildTypes{
register("debug") {
filters(
customRibbon(label = "Debug", ribbonColor = "#FF0000")
)
}
register("release") {
filters(
customRibbon(label = "BETA", ribbonColor = "#F18357")
)
}
}
}

Maven Publish KTS - SourceSet with name 'main' not found

I want to create a script for publish a library on kotlin.
I am trying to convert this gradle config to gradle-kotlin (kts) following the gradle documentation (https://docs.gradle.org/current/userguide/publishing_maven.html#header) but I have this error when project builds:
FAILURE: Build failed with an exception.
Where:
Script '.../gradle-mvn-push.gradle.kts' line: 18
SourceSet with name 'main' not found.
Here is the script in kts:
Kotlin version : 1.6.10
Kotlin-gradle plugin version : 7.2.1
apply(plugin = "maven-publish")
var gitLabPrivateToken: String? = null
val secretPropertiesFile = rootProject.file("example.properties")
if (secretPropertiesFile.exists()) {
val secretProperties = java.util.Properties().apply {
load(java.io.FileInputStream(secretPropertiesFile))
}
gitLabPrivateToken = secretProperties["token"].toString()
}
val gitlabUrl = "exampleUrl"
lateinit var sourcesArtifact: PublishArtifact
tasks {
val sourcesJar by creating(Jar::class) {
archiveClassifier.set("sources")
from(project.the<SourceSetContainer>()["main"].allSource)
}
artifacts {
sourcesArtifact = add("archives", sourcesJar)
}
}
afterEvaluate {
configure<PublishingExtension> {
publications {
create<MavenPublication>("maven") {
artifact(sourcesArtifact) // Adds source as separate jar.
groupId = "com.example"
artifactId = "lib"
version = "0.1.0"
from(components["release"])
}
}
repositories {
maven {
name = "GitLabPersonal"
url = uri(gitlabUrl)
credentials(HttpHeaderCredentials::class) {
name = "Private-Token"
value = gitLabPrivateToken
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
maven {
name = "GitLabCi"
url = uri(gitlabUrl)
credentials(HttpHeaderCredentials::class) {
name = "Job-Token"
value = System.getenv("CI_JOB_TOKEN")
}
authentication {
create<HttpHeaderAuthentication>("header")
}
}
}
}
}

Setting appDistributionProperties.apkPath from Kotlin dsl

I've tried converting our Android app to use the Kotlin DSL for gradle and I can't get AppDistribution to work in my CI build. This is the error I got:
App Distribution found more than 1 output file for this variant. Please contact firebase-support#google.com for help using APK splits with App Distribution.
This is what was working in groovy:
applicationVariants.all { variant ->
variant.outputs.each { output ->
tasks.findAll {
it.name.startsWith(
"appDistributionUpload${variant.name.capitalize()}")
}.each {
it.doFirst {
it.appDistributionProperties.apkPath = output.outputFile.absolutePath
}
}
}
}
I can't find a way to set appDistributionProperties.apkPath in the kotlin dsl:
applicationVariants.forEach { variant ->
variant.outputs.forEach { output ->
tasks.filter {
it.name.startsWith("appDistributionUpload${variant.name.capitalize()}")
}.forEach {
it.doFirst {
it.setProperty("apkPath", output.outputFile.absolutePath)
}
}
}
}
I'm guessing I need a magic string instead of just "apkPath", because there doesn't seem to exist a strongly typed way of saying this.
Kotlin dsl way:
android.applicationVariants.all {
this.outputs.all {
val output = this
tasks.matching {
it.name.startsWith(
"appDistributionUpload${this.name.capitalize()}"
)
}.forEach {
it.doFirst {
if (it is com.google.firebase.appdistribution.gradle.UploadDistributionTask) {
it.appDistributionProperties.get().apkPath = output.outputFile.absolutePath
}
}
}
}
}

Unresolved reference trying to build .aar with kotlin mpp

I am trying to get https://github.com/Kotlin/kotlinx.serialization to work with my android library I am building using kotlin mpp. This is my gradle file
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.multiplatform")
kotlin("plugin.serialization") version "1.3.61"
}
group = "org.greeting"
version = 1.0
android {
compileSdkVersion(27)
defaultConfig {
minSdkVersion(15)
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
dependencies {
// Specify Kotlin/JVM stdlib dependency.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
implementation(kotlin("stdlib", org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)) // or "stdlib-jdk8"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0") // JVM dependency
testImplementation("junit:junit:4.12")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
androidTestImplementation("junit:junit:4.12")
androidTestImplementation("org.jetbrains.kotlin:kotlin-test")
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit")
}
kotlin {
android("androidLib")
val buildForDevice = project.findProperty("device") as? Boolean ?: false
val iosTarget = if (buildForDevice) iosArm64("ios") else iosX64("ios")
iosTarget.binaries {
framework {
// Disable bitcode embedding for the simulator build.
if (!buildForDevice) {
embedBitcode("disable")
}
}
}
sourceSets["androidLibMain"].dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.14.0")
}
sourceSets {
commonMain {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.14.0")
}
}
commonTest {
dependencies {
implementation("org.jetbrains.kotlin:kotlin-test-common")
implementation("org.jetbrains.kotlin:kotlin-test-annotations-common")
}
}
}
}
tasks.register("copyFramework") {
val buildType = project.findProperty("kotlin.build.type") as? String ?: "DEBUG"
dependsOn("link${buildType.toLowerCase().capitalize()}FrameworkIos")
doLast {
val srcFile = (kotlin.targets["ios"] as KotlinNativeTarget).binaries.getFramework(buildType).outputFile
val targetDir = project.property("configuration.build.dir")!!
copy {
from(srcFile.parent)
into(targetDir)
include("greeting.framework/**")
include("greeting.framework.dSYM")
}
}
}
tasks.register("iosTest") {
val device = project.findProperty("iosDevice") as? String ?: "iPhone 8"
dependsOn("linkDebugTestIos")
group = JavaBasePlugin.VERIFICATION_GROUP
description = "Runs tests for target 'ios' on an iOS simulator"
doLast {
val binary = (kotlin.targets["ios"] as KotlinNativeTarget).binaries.getTest("DEBUG").outputFile
exec {
commandLine("xcrun", "simctl", "spawn", "--standalone", device, binary.absolutePath)
}
}
}
When I try to run build i get Unresolved reference: serialization on line import kotlinx.serialization.ImplicitReflectionSerializer; But if compile the androidTests the linking works fine. Strangely i have to do sourceSets["androidLibMain"] and can't include it in the sourceSet{} block not sure if this is related. Any ideas on why the build task is not linking my serialization library? Thanks

Modify all flavors .class in gradle

I have many flavors in my app.gradle. When I exec gradlew assembleRelease I can find all flavors class in dir app/build/intermediates/classes. Now, I want to modify class(do my task) after class generated. How can I do that?
My task name is m.
gradle.projectsEvaluated {
def buildTypes = android.buildTypes.collect { type -> type.name }
def productFlavors = android.productFlavors.collect { flavor -> flavor.name }
// When no product flavors defined, use empty
if (!productFlavors) {
productFlavors.add('')
}
productFlavors.each { productFlavorName ->
buildTypes.each { buildTypeName ->
def flavorNameCapitalized = "${productFlavorName.capitalize()}"
def buildNameCapitalized = "${buildTypeName.capitalize()}"
def targetName = "${flavorNameCapitalized}${buildNameCapitalized}"
def preTaskName = "compile${targetName}JavaWithJavac"
def nextTaskName = "compile${targetName}Ndk"
if (!isDebug(buildNameCapitalized.toString())) {
project.tasks.findByName("generate${targetName}Sources").doLast { //generate${targetName}Sources
println("projectsEvaluated flavorName: " + flavorNameCapitalized.toString())
println("projectsEvaluated buildName: " + buildNameCapitalized.toString())
project.ext.set("flavorName", flavorNameCapitalized.toString());
project.ext.set("buildName", buildNameCapitalized.toString());
}
m.dependsOn project.tasks.findByName(preTaskName)
project.tasks.findByName(nextTaskName).dependsOn m
}
}
}
}
But my task only run once. Help
finally, I solved with dynamic task
String newTaskName = "${targetName}m";
task "$newTaskName"() << {
...
}
def newTask = project.tasks.findByName("$newTaskName")
newTask.dependsOn project.tasks.findByName(preTaskName)
project.tasks.findByName(nextTaskName).dependsOn newTaskName

Categories

Resources