How to add dependency using a method in Gradle - android

I can add a module dependency using either of
aar dependency
local project module
Right now I am able to accomplish same using below
if (foo(":awesomemodule")) {
implementation 'com.example.app:awesomemodule:1.0'
}
else {
implementation project(':awesomemodule')
}
Now I want to repeat this code for multiple dependencies and want to create a method for same.
I want something like this
customAddImplementation(':awesomemodule')
ext.customAddImplementation = { moduleName ->
if (foo(moduleName)) {
return implmentation('com.example.app' + moduleName + ':1.0')
} else {
return project(path: moduleName)
}
}
But this approach is not working, as implementation() definition is not found when i add my dependencies using customAddImplementation(':awesomemodule')

Something like this should work:
def customModulePath(String moduleName) {
if (foo(moduleName)) {
return "com.example.app:$moduleName:1.0")
} else {
return project(":$moduleName")
}
}
dependencies {
implementation(customModulePath('awesomemodule'))
}
And bonus point: since the function doesn't try to add the custom module path to the implementation dependencies, but simply returns its path, you can ruse it to add dependencies to any other configuration.

Related

gradle kotlin dsl: how to create a shared function which uses a plugin class?

A simplified child module build.gradle.kts:
plugins {
id("com.android.library")
kotlin("android")
}
android {
androidComponents.beforeVariants { it: com.android.build.api.variant.LibraryVariantBuilder ->
it.enabled = run {
// logic to calculate if
it.productFlavors[0].second == "flavor" && it.buildType == "debug"
}
}
}
Is it possible to extract function for calculation of enabled state of buildVariant?
fun calculateIsEnabled(lvb: com.android.build.api.variant.LibraryVariantBuilder): Boolean {
return lvb.productFlavors[0].second == "flavor" && lvb.buildType == "debug"
}
I tried to declare the function in the root build.gradle.kts but I don't know how to access it from submodule and if it is possible at all
I tried to declare it in buildSrc module, but com.android.build.api.variant.LibraryVariantBuilder is undefined here because the plugin com.android.library is not present here and I think it is not allowed and/or meaningless
So, the question is: where to declare a shared function that uses types defined in a gradle plugin and need to be accessible in all submodules of type android library?
After several tries I solved it:
buildSrc/build.gradle.kts
repositories {
google()
mavenCentral()
}
plugins {
`kotlin-dsl`
}
dependencies {
// important: dependency only in simple string format!
implementation("com.android.tools.build:gradle:7.2.0-alpha03")
}
buildSrc/src/main/kotlin/Flavors.kt
import com.android.build.api.variant.LibraryVariantBuilder
import com.android.build.api.variant.ApplicationVariantBuilder
private fun isFlavorEnabled(flavor1: String, buildType: String): Boolean {
return flavor1 == "flavor" && buildType == "debug"
}
fun isFlavorEnabled(lvb: LibraryVariantBuilder): Boolean {
// productFlavors are pairs of flavorType(dimension) - flavorName(selectedFlavor)
return lvb.run { isFlavorEnabled(productFlavors[0].second, buildType ?: "") }
}
fun isFlavorEnabled(avb: ApplicationVariantBuilder): Boolean {
return avb.run { isFlavorEnabled(productFlavors[0].second, buildType ?: "") }
}
In library/build.gradle.kts and app/build.gradle.kts
android {
androidComponents.beforeVariants {
it.enabled = isFlavorEnabled(it)
}
}

Using Android library in Kotlin multiplatform library

I have three targets commonMain/androidMain/iOSMain respectively. Because I need to access the assets in Android devices in androidMain module. I found I cannot use the Android API... The following is part of my build.gradle.kts:
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
id("com.android.application")
kotlin("multiplatform")
}
repositories {
google()
jcenter()
}
android {
compileSdkVersion(29)
buildToolsVersion("29.0.1")
defaultConfig {
minSdkVersion(19)
targetSdkVersion(29)
}
sourceSets {
getByName("main") {
manifest.srcFile("src/androidMain/AndroidManifest.xml")
java.srcDirs(file("src/androidMain/kotlin"))
res.srcDirs(file("src/androidMain/res"))
}
}
}
kotlin {
//select iOS target platform depending on the Xcode environment variables
val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =
if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)
::iosArm64
else
::iosX64
iOSTarget("ios") {
binaries {
framework {
baseName = "Example"
}
}
}
jvm("android")
sourceSets["commonMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
}
sourceSets["androidMain"].dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib")
}
sourceSets["commonTest"].dependencies {
implementation ("org.jetbrains.kotlin:kotlin-test")
implementation ("org.jetbrains.kotlin:kotlin-test-junit")
}
}
How can I use Android library in androidMain? For example,
val inputStream = assets.open("Test.txt")
Make sure to create an AndroidManifest.xml with (library module) package name different than the (app module) package name underneath the SharedCode/src directory. And as Nagy Robi said use android() instead of jvm('android'). Please check the below references also:
Adding an Android Target to a Kotlin Multiplatform Project
Multiplatform sample
Try using android() instead of jvm('android') to load the target from the presets.

No such property: betaDistributionApkFilePath for class: java.lang.String

I have found little glitch in crashlytics/fabric setup in gradle which took 2 hours of my poor life.
first of all i did everything they said in this guide:
https://fabric.io/kits/android/crashlytics/install
and here is the piece of code which everything caused:
First try
dependencies {
...
compile('com.crashlytics.sdk.android:crashlytics:2.9.4#aar') {
transitive = true;
}
}
every library version in dependencies{} block iam using ext{} block in appname/app/build.gradle so in our case
ext {
crashlytics = '2.9.4#aar'
}
so in the end it will be like this only change implementation which should be there in my opinion because it will be deprecated at end of 2018. implementations doenst change any behavior.
implementation("com.crashlytics.sdk.android:crashlytics:$crashlytics"){
transitive = true
}
but if u try to build this u gonna get:
No such property: betaDistributionApkFilePath for class: java.lang.String
wow. i have no idea why.. but lets investigate this. we will try different setup so forget everyting you saw above.
Second try
in our appname/app/build.gradle in dependencies{} change our lane to this so we have no ext{} for version
implementation("com.crashlytics.sdk.android:crashlytics:$rootProject.ext.crashlytics") {
transitive = true
}
and now in our root gradle appname/build.gradle
buildscript {
ext.fabric_gradle = '2.9.4#aar'
repositories {
...
}
dependencies {
...
}
}
Build is OK you can continue to work. But what? Its should be completely the same...
Third try
I dont know why but in our first try u just CANT in appname/app/build.gradle ext{} block name crashlytics variable it have to very everything else except crashlytics for instance crashlytics_version so lets make first try to working state.
ext {
crashlytics_version = '2.9.4#aar'
}
implementation("com.crashlytics.sdk.android:crashlytics:$crashlytics_version"){
transitive = true
}
Build is OK. Magic.
or just do it in normal way and dont try to make smart things.... As first block of code in this long investigation which works also completely fine:
compile('com.crashlytics.sdk.android:crashlytics:2.9.4#aar') {
transitive = true;
}
You just CANT in appname/app/build.gradle ext{} block name your variable crashlytics it have to be everything else except crashlytics for instance crashlytics_version so:
ext {
crashlytics_version = '2.9.4#aar'
}
implementation("com.crashlytics.sdk.android:crashlytics:$crashlytics_version"){
transitive = true
}
Build is OK.
or just do it in normal way:
compile('com.crashlytics.sdk.android:crashlytics:2.9.4#aar') {
transitive = true;
}

Gradle dynamic flavor

I would like to create dynamic flavors from the directory tree.
It works great!
But Android Studio uses gradle in its tmp file like:
/home/svirch_n/.IntelliJIdea14/system/compile-server
and my script doesn't work anymore because it uses relative paths like this:
Closure getFlavors = { rootDir, basePackage ->
def result = [:]
new File("$rootDir").eachDir() { dir ->
def name = dir.getName()
if ("$name" != "main")
result.put("$name", "$basePackage.$name")
}
return result
}
// This is an ugly closure.
// If I can get rid of this, my problem will be solved
Closure getSrcPath = {
if (System.getProperty("user.dir").split("/").last() == "app") {
return "src"
} else {
return "app/src"
}
}
android {
...
def myFlavors = getFlavors(getSrcPath(), "com.example.app")
productFlavors {
myFlavors.each { flavorName, flavorPackage ->
"$flavorName" {
applicationId "$flavorPackage"
}
}
}
}
Do you have an idea how to solve this?
Thanks in advance for your help
P.S: I want dynamic flavors cause my git project has public and private repositories and not everyone can have all the flavors but I want them to compile anyway.
Assuming I am in the subproject 'app', I can use:
project(":app").getProjectDir().getPath()

NoSuchMethodError using Google Endpoints

I am creating an Android app using Google App Engine. In order to use GCM (Google Cloud Messaging), I have created a GCM module in Android Studio. This module provides a sample code that registers devices in the Datastore.
All was working well yesterday, and although nothing changed, I have this error when I try to register my device :
java.lang.NoSuchMethodError: com.google.appengine.api.datastore.Cursor: method <init>()V not found
I don't know what exactly means the notation <init>()V, but I found the Cursor class to be generated by the Google plugin of Android Studio, here :
This is the decompiled code inside Cursor.class :
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.google.appengine.api.datastore;
import java.io.Serializable;
public final class Cursor implements Serializable {
private String webString;
public Cursor(String webString) {
this.webString = webString;
}
public String toWebSafeString() {
return this.webString;
}
public static Cursor fromWebSafeString(String encodedCursor) {
if(encodedCursor == null) {
throw new NullPointerException("encodedCursor must not be null");
} else {
return new Cursor(encodedCursor);
}
}
public boolean equals(Object o) {
if(this == o) {
return true;
} else if(o != null && this.getClass() == o.getClass()) {
Cursor cursor = (Cursor)o;
return this.webString.equals(cursor.webString);
} else {
return false;
}
}
public int hashCode() {
return this.webString.hashCode();
}
public String toString() {
return this.webString;
}
}
Finally, this is my build.gradle :
// If you would like more information on the gradle-appengine-plugin please refer to the github page
// https://github.com/GoogleCloudPlatform/gradle-appengine-plugin
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
}
}
repositories {
jcenter();
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'
sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
compile 'com.google.appengine:appengine-endpoints:1.9.18'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
compile 'javax.servlet:servlet-api:2.5'
compile 'com.googlecode.objectify:objectify:4.0b3'
compile 'com.ganyo:gcm-server:1.0.2'
}
appengine {
downloadSdk = true
appcfg {
oauth2 = true
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}
Because I changed nothing in the concerned code, I really can't understand what happened and I found nothing useful on the web.
Thank you in advance for your help.
Edit : StackTrace from the backend log
It's looking for the empty constructor : method init()v not found
It appears this could be because Cursor.java is pulled from your <module>/build/classes/main (or <module>/build/exploded-app/WEB-INF/classes/main), when really it should just be pulled in from a library appengine-api-1.0-sdk-<version>.jar.
Have you added the source for a Cursor.java into your project src folder somehow? The App Engine build creates a runnable build at <module>/build/exploded-app and the cursor class is usually sourced from <module>/build/exploded-app/WEB-INF/lib/appengine-api-1.0-sdk-<version>.jar

Categories

Resources