skip to Main Content

I have a java Android project and I am trying to include Kotlin/Convert some of the java classes to Kotlin.

Project’s build.gradle:

Here I have introduced a variable for Kotlin version 1.6.10 and kotlin gradle plugin.

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    
    import com.tcs.dev.Repo
    import org.gradle.wrapper.SystemPropertiesHandler
    
    buildscript {
        ext.kotlin_version = '1.6.10'
    
        // load local.properties file like gradle.properties
        System.getProperties().putAll(SystemPropertiesHandler.getSystemProperties(file('local.properties')))
        Properties properties = new Properties()
        if (file("local.properties").exists()) {
           properties.load(file('local.properties').newDataInputStream())
        }
        if (project.hasProperty('propfile')) {
           properties.load(file(propfile).newDataInputStream())
        }
        properties.each { k, v -> ext[k] = v }
    
        repositories {
            google()
    
            maven Repo.config(project)
            maven { url 'https://jitpack.io' }
            maven { url "https://oss.sonatype.org/content/repositories/snapshots" } // For Spoon snapshot, until 2.0.0 is released
    
            jcenter()
    
        }
    
        dependencies {
    
            classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
    
            classpath 'com.android.tools.build:gradle:7.0.4'    
            classpath 'com.google.gms:google-services:4.3.10'
    
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    
        }
    }

App’s build.gradle:

Here I have introduced the Kotlin plugin and two more Kotlin libraries in the implementation section.

    import com.tcs.dev.BuildVars
    import com.tcs.dev.FileExtension
    import com.tcs.dev.PropertiesFile
    import com.tcs.dev.Repo
    import com.tcs.dev.Shell
    import com.tcs.dev.Version
    
    import java.nio.file.Files
    import java.nio.file.Paths
    import java.util.regex.Pattern
    
    buildscript {
        // Items referenced both in and outside of the buildscript block
        ext {
            buildToolsDir           = System.env.get('HOME') + "/build_tools"
            localProperties         = new File(rootProject.projectDir, "local.properties")
        }
    
        // SDK - needs to be done by buildscript because the android-sdk-manager plugin requires it on apply
        def androidSdkName          = getProperty('systemProp.tcs.dev.android.sdk.name')
        def androidSdkVersion       = getProperty('systemProp.tcs.dev.android.sdk.version')
        def androidSdkClassifier    = "darwin-x86_64"
        def androidSdkExt           = "tgz"
    
        repositories {
            jcenter()
            google()
            maven Repo.config(project)
        }
    
        dependencies {
            classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
            classpath group: 'com.google', name: androidSdkName,
                    version: androidSdkVersion, ext: androidSdkExt, classifier: androidSdkClassifier
        }
    
        PropertiesFile.addOrChangeKey(project.localProperties, "sdk.dir", androidSdkDir)
    
        // This should already exist, but just in case not
        FileExtension.mkdirs(project.buildToolsDir)
    
        // Extract the Android SDK
        if (! new File(sdkBomFile).exists()) {
            def sdkFile = sprintf("%s-%s-%s.%s", androidSdkName, androidSdkVersion, androidSdkClassifier, androidSdkExt)
            def sdkFullFile = buildscript.configurations.classpath.files.find {
                if (it.getName() == sdkFile) return it.getAbsoluteFile()
            }
            println "Extracting Android SDK"
            def cmd = "tar -zxf ${sdkFullFile} -C ${project.buildToolsDir}"
            def (exitValue, output) = Shell.shellCommandReturningExitValueAndOutput(cmd)
            if (exitValue != 0) {
                throw new Exception("Extract command exited with '${exitValue}': ${cmd}")
            }
            if (! new File(sdkBomFile).exists()) {
                throw new Exception("Extract command did not create file '${sdkBomFile}': ${cmd}")
            }
            println "Accepting license agreements"
            cmd = "yes | ${androidSdkDir}/tools/bin/sdkmanager --sdk_root=${androidSdkDir} --licenses"
            (exitValue, output) = Shell.shellCommandReturningExitValueAndOutput(cmd)
        }
    }
    
    
    plugins {
        id 'com.android.application'
        id 'kotlin-android'
        id 'com.google.firebase.crashlytics'
        id 'com.google.gms.google-services'
    }
    
    // NDK
    def androidNdkName      = getProperty('systemProp.tcs.dev.android.ndk.name')
    def androidNdkVersion   = getProperty('systemProp.tcs.dev.android.ndk.version')
    def androidNdkDir       = sprintf("%s/%s-%s", project.buildToolsDir, androidNdkName, androidNdkVersion)
    def androidNdkInstalled = {
        return new File(androidNdkDir, 'ndk-build').exists()
    }
    // AVD
    def androidAvdName      = getProperty('systemProp.tcs.dev.android.avd.name')
    def androidAvdVersion   = getProperty('systemProp.tcs.dev.android.avd.version')
    def androidAvdDir       = sprintf("%s/%s-%s", project.buildToolsDir, androidAvdName, androidAvdVersion)
    def androidAvdInstalled = {
        return new File(androidAvdDir, "Nexus_9_API_${androidAvdVersion}.ini").exists()
    }
    
    
    [graphicsLibVersion:graphicsLibVersion, serializableLibVersion:serializableLibVersion].each { k, v ->
      if (!v.startsWith(Version.projectVersion())) {
        logger.warn "WARNING: ${k} ${v} does not match projectVersion ${Version.projectVersion()} please update"
      }
    }
    
    
    def gitSha() {
        return 'git rev-parse --short HEAD'.execute().text.trim()
    }
    
    configurations {
        graphicsLib
        eigen
        generatedSource
        manuallyEditedSourceDependencies
        if (!androidNdkInstalled()) {
            androidNdk
        }
        if (!androidAvdInstalled()) {
            androidAvd
        }
    }
    
    dependencies {
        graphicsLib group: 'com.tcs', name: 'graphics-lib',
                version: graphicsLibVersion, ext: 'tgz', classifier: 'sources'
        generatedSource group: 'com.tcs', name: 'tcs-serializable-generator',
        manuallyEditedSourceDependencies  group: 'com.tcs', name: 'cppGraphicsDependencies',
                version: cppGraphicsVersion, ext: 'tgz', classifier: 'sources'
        if (!androidNdkInstalled()) {
            androidNdk group: 'com.google', name: androidNdkName,
                    version: androidNdkVersion, ext: 'zip', classifier: 'darwin-x86_64'
        }
        if (!androidAvdInstalled()) {
            androidAvd group: 'com.google', name: androidAvdName,
                    version: androidAvdVersion, ext: 'tgz', classifier: 'darwin-x86_64'
        }
    }
    
    def minutesSinceEpoch() {
        // This should produce unique values for > 4000 years
        def minutes = new Date().time.intdiv(1000).intdiv(60).intdiv(30) * 30
        Integer clamped = minutes & Integer.MAX_VALUE
        return clamped
    }
    
    android {
    
        compileSdkVersion 30
        buildToolsVersion '30.0.0'
    
        // Required for butterknife compatibility with androidx
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    
        buildFeatures {
            viewBinding true
        }
    
        defaultConfig {
            applicationId = BuildVars.applicationId
            minSdkVersion 21
            targetSdkVersion 30
            versionCode minutesSinceEpoch()
            versionName "${Version.packageVersion()}"
            testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
            multiDexEnabled true
            vectorDrawables.useSupportLibrary = true
    
        }
    
        sourceSets.main {
            // use the jni .so compiled from the manual ndk-build command'''
            jniLibs.srcDirs = ['src/main/jniLibs/debug/lib', 'src/main/jniLibs/release/lib']
            jni.srcDirs = [] //disable automatic ndk-build call
    
        }
    
        signingConfigs {
            debug {
                storeFile file("../tcs-debug.keystore")
                storePassword "dummy1"
                keyAlias "debug"
                keyPassword "dummy1"
            }
            release {
                storeFile file("../tcs-android.keystore")
                storePassword project.properties.get("dummy2")
                keyAlias "release"
                keyPassword project.properties.get("dummy2")
            }
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
                signingConfig signingConfigs.release
    
            }
            debug {
                signingConfig signingConfigs.debug
                applicationIdSuffix ".debug"
                jniDebuggable true
                ext.setBuildConfigFieldWithDefault = { fieldName, value ->
                    if (project.hasProperty(fieldName)) {
                        value = project.properties.get(fieldName)
                    }
                    buildConfigField "String", fieldName, ""$value""
                }
    
                def testMap = []
                if (project.findProperty('testStack') == "staging") {
                    testMap = [
                            'testServer'            :'https://staging.dev.tcs.com/',
                            
                            'testAsmBrokenTestDocId':'a63f4467861f1c54500afd9d',
                            'testAsmBrokenTestWsId' :'9b6b7102fe2578a1d683adf1',
                            'testAnonymousDocId'    :'346fa02ef804498723df9b6e'
                    ]
                } else {
    
                    testMap = [
                            'testServer'            :'https://demo-c.dev.tcs.com/',
                            
                    ]
                }
                printf("testMap is: ${testMap}n")
                testMap.each{ k, v -> setBuildConfigFieldWithDefault(k, v) }
    
                setBuildConfigFieldWithDefault("testUsername", "[email protected]")
                setBuildConfigFieldWithDefault("testPassword", "testPassword")
                ext.enableCrashlytics = false
                pseudoLocalesEnabled true
                testCoverageEnabled true
            }
        }
    
        project.gradle.taskGraph.whenReady {
            connectedDebugAndroidTest {
                ignoreFailures = true
            }
        }
    
        packagingOptions {
            exclude 'META-INF/rxjava.properties'
            exclude 'META-INF/LICENSE'
            exclude 'META-INF/NOTICE'
            exclude 'META-INF/LICENSE.txt'
            exclude 'META-INF/NOTICE.txt'
            exclude 'LICENSE.txt'
            doNotStrip "*/armeabi/*.so"
            doNotStrip "*/armeabi-v7a/*.so"
            doNotStrip "*/x86/*.so"
        }
    
        dexOptions {
            javaMaxHeapSize "2g"
        }
    
        applicationVariants.all { variant ->
            variant.mergeAssetsProvider.get().dependsOn(extractShaders)
        }
    
        lintOptions {
            disable 'MissingTranslation', 'ExtraTranslation', 'NotSibling'
        }
    }
    
    android.applicationVariants.all { variant ->
        def applicationId = variant.applicationId as String
    
        //def adbPath = android.adbExe as String
        def adbPath = "/platform-tools/adb"
    
        def variantName = variant.name.capitalize()
        String pd = projectDir as String
        def adbPlus = pd + "/../buildSrc/adb+.sh"
    
        def grantPermissionsTask = tasks.create("grant${variantName}Permissions") {
            doLast {
                "bash ${adbPlus} ${adbPath} ${applicationId} android.permission.READ_CONTACTS".execute()
                "bash ${adbPlus} ${adbPath} ${applicationId} android.permission.WRITE_EXTERNAL_STORAGE".execute()
            }
        }
    
        grantPermissionsTask.description = "Grants permissions on Marshmallow and later"
        grantPermissionsTask.group = "extras"
    }
    
    tasks.whenTaskAdded { theTask ->
        if (theTask.name.equals("compileReleaseJavaWithJavac")) {
            theTask.dependsOn "ndkBuildRelease"
        } else if (theTask.name.equals("compileDebugJavaWithJavac")) {
            theTask.dependsOn "ndkBuildDebug"
        }
    }
    
    def assetsDir = new File(projectDir, 'src/main/assets')
    
    task createAssetsDir() {
        doFirst {
            FileExtension.mkdirs(assetsDir)
        }
    }
    
    task extractEigen(type: Copy) {
        description = 'Expand eigen files into $EIGEN_DIR'
    
        from(tarTree(configurations.eigen.singleFile)) {
            include '*/Eigen/*'
            include '*/Eigen/src/**'
        }
        if (eigenVersion == '506565787cdc4') { // 3.1.2
            into eigenDir
            def root = Pattern.compile('.*/Eigen/')
            eachFile {
                it.mode |= 0220
                it.path = it.path.replaceFirst(root, '')
            }
        } else {
            into eigenDir
            eachFile {
                it.mode |= 0220
            }
        }
        dirMode 0775
    }
    
    task extractShaders(type: Exec) {
        dependsOn createAssetsDir
    
        def wd = assetsDir
        def extractDir = 'shaders'
        def targetFile = 'shaders'
    
        inputs.file configurations.graphicsLib.singleFile
    
        // Ensure the shaders are extracted
        outputs.upToDateWhen { false }
    
        outputs.dir "${wd}/${extractDir}"
    
        workingDir wd
        commandLine 'tar', '-s', ",graphics-lib-${graphicsLibVersion}/${targetFile},${extractDir},", '-xzf', configurations.graphicsLib.singleFile, "graphics-lib-${graphicsLibVersion}/${targetFile}"
    }
    
    task extractGraphics(type: Exec) {
        description = 'set property graphics.repo.dir if you want to use your own version of the graphics repo.' +
          ' e.g. put a line like this in local.properties: graphics.repo.dir=/Users/pkania/repos/master/graphics'
    
        def wd = 'src/main/jni'
        def extractDir = 'Graphics'
        def targetFile = 'GraphicsLibrary'
        def graphicsDir = "${wd}/${extractDir}"
    
        // Ensure the graphics are extracted
        outputs.upToDateWhen { false }
    
        outputs.dir graphicsDir
    
        workingDir wd
    
        doFirst {
            def path = Paths.get(graphicsDir)
            if (Files.isSymbolicLink(path)) {
                Files.delete(path)
            }
        }
    
        def graphicsRepoDir = project.properties.get('graphics.repo.dir')
        if (graphicsRepoDir) {
            def script = """
    if [ -d ${extractDir} ]; then
        rm -rf ${extractDir}
    fi
    ln -sf ${graphicsRepoDir}/BTGraphicsLibrary ${extractDir}
    """
            commandLine Shell.getShellCommandLine(script)
        } else {
            inputs.file configurations.graphicsLib.singleFile
            commandLine 'tar', '-s', ",graphics-lib-${graphicsLibVersion}/${targetFile},${extractDir},", '-xzf', configurations.graphicsLib.singleFile, "graphics-lib-${graphicsLibVersion}/${targetFile}"
        }
    }
    
    task getGeneratedSource(type:Exec) {
        description = 'extract cppGraphics generated source'
        dependsOn extractGraphics
        def tarFile = configurations.generatedSource.singleFile.path
        inputs.file tarFile
        workingDir 'src/main/jni'
        // Ensure the cpp graphics are extracted
        outputs.upToDateWhen { false }
        def cmd = Shell.getShellCommandLine("tar xf ${tarFile}")
        commandLine cmd
    }
    
    task getManuallyEditedSourceDependencies(type:Exec) {
        description = 'extract cppGraphics manually edited source'
        // this task must run after the generated sources are copied because manually edited files can
        // overwrite generated ones.
        dependsOn getGeneratedSource
        def tarFile = configurations.manuallyEditedSourceDependencies.singleFile.path
        inputs.file tarFile
        workingDir 'src/main/jni/cppGraphics'
        def cmd = Shell.getShellCommandLine("tar xf ${tarFile}")
        commandLine cmd
    }
    
    task getAndroidNdkDir(type:Exec) {
    
    }
    getAndroidNdkDir.onlyIf {
        !androidNdkInstalled()
    }
    
    task getAndroidNdk() {
    
    }
    
    task createSwigOutputDir {
        doLast {
            def swigOutputDir = file('src/main/java/com/tcs/app/graphics/gen')
            FileExtension.mkdirs(swigOutputDir)
        }
    }
    
    task getAndroidAvdDir(type:Exec) {
    
    }
    getAndroidAvdDir.onlyIf {
        !androidAvdInstalled()
    }
    
    task getAndroidAvd() {
    }
    
    task swigBuild(type: Exec) {
        dependsOn extractGraphics
        dependsOn getGeneratedSource
        dependsOn getManuallyEditedSourceDependencies
        dependsOn createSwigOutputDir
        dependsOn extractEigen
        workingDir 'src/main/jni'
        commandLine '/usr/local/bin/swig', '-java', '-c++', '-package', 'com.tcs.app.graphics.gen', '-outdir', '../java/com/tcs/app/graphics/gen', '-o', './graphics_wrap.cpp', 'graphics.i'
    }
    
    def numCompilationThreads = {
        def (exitValue, numCompileThreads) = Shell.shellCommandReturningExitValueAndOutput("sysctl -n hw.ncpu");
        if (exitValue != 0) {
            return 1
        }
    
        return numCompileThreads
    }
    
    // call regular ndk-build(.cmd) script from app directory
    // http://stackoverflow.com/questions/16667903/android-studio-gradle-and-ndk
    // http://ph0b.com/android-studio-gradle-and-ndk-integration/
    
    // TODO: We'd rather not have two tasks here.  Either research whether we can just use the default NDK build with our own Android.mk, or figure out how to streamline this w/Gradle.
    task ndkBuildDebug(type: Exec) {
    
    }
    
    
    task ndkBuildRelease(type: Exec) {
    
    }
    
    // TODO: do not brute force delete the graphics generated files. Instead, tell gradle they are output files, and
    // let gradle automatically clean them
    task cleanGraphicsGen(type: Delete) {
    
    }
    
    clean.dependsOn(cleanGraphicsGen)
    
    // TODO: This is a more complete way to get the dependencies in place, but we need to figure out how to get the
    // TODO: buildtype so we can refer to the correct ndkBuild task...
    //tasks.withType(JavaCompile) {
    //    compileTask -> compileTask.dependsOn ndkBuild
    //}
    
    dependencies {
    
        implementation 'androidx.core:core-ktx:1.3.2'
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    
        implementation 'com.google.android.material:material:1.1.0-alpha08'
        implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
    
        implementation 'androidx.viewpager:viewpager:1.0.0'
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        
        implementation("com.tcs:tcs-android:$serializableLibVersion") {
            transitive = false
        }
        implementation("com.tcs:tcs-primogenitor:$serializableLibVersion") {
            transitive = false
        }
        implementation("com.tcs:tcs-serialize-common:$serializableLibVersion") {
            transitive = false
        }
    
        coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
        implementation 'androidx.legacy:legacy-support-v13:1.0.0'
        implementation 'androidx.appcompat:appcompat:1.0.0'
        implementation 'androidx.recyclerview:recyclerview:1.0.0'
        implementation 'androidx.cardview:cardview:1.0.0'
        implementation 'com.google.android.material:material:1.0.0'
        implementation 'androidx.percentlayout:percentlayout:1.0.0'
        implementation 'com.google.android.gms:play-services-analytics:16.0.8'
        implementation 'com.google.firebase:firebase-core:18.0.0'
        implementation 'com.google.firebase:firebase-analytics:18.0.0'
        implementation "com.google.firebase:firebase-messaging:17.5.0"
    
        annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
        implementation 'com.squareup.picasso:picasso:2.5.2'
        implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
        implementation 'com.squareup.retrofit2:retrofit:2.9.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
        implementation 'com.squareup.retrofit2:converter-jackson:2.0.0'
        implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
        implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
        implementation 'com.google.code.gson:gson:2.6.2'
    
        implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.0"))
    
        // define any required OkHttp artifacts without version
        implementation("com.squareup.okhttp3:okhttp")
        implementation("com.squareup.okhttp3:logging-interceptor")
    
        implementation("com.google.guava:guava:31.0.1-android")
    
        implementation 'io.reactivex:rxandroid:1.2.1'
        implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
        implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
    
        implementation 'commons-lang:commons-lang:2.6'
        implementation 'org.slf4j:slf4j-api:1.7.13'
        implementation 'com.melnykov:floatingactionbutton:1.3.0'
        implementation 'com.getbase:floatingactionbutton:1.10.1'
    
        implementation 'net.danlew:android.joda:2.9.9.4'
        implementation 'io.github.inflationx:calligraphy3:3.1.1'
        implementation 'io.github.inflationx:viewpump:2.0.3'
]        
    
        implementation 'com.google.firebase:firebase-crashlytics:17.2.2'
    
        androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1') {
            exclude module: 'support-annotations'
        }
    
        androidTestImplementation('androidx.test.espresso:espresso-idling-resource:3.1.1') {
            exclude module: 'support-annotations'
        }
    
        androidTestImplementation "com.squareup.spoon:spoon-client:2.0.0-SNAPSHOT" // For Spoon snapshot, until 2.0.0 is released
    
        androidTestImplementation 'androidx.test:rules:1.1.1'
        androidTestImplementation 'androidx.test.ext:junit:1.1.0'
        androidTestImplementation 'androidx.test:runner:1.1.1'
        androidTestImplementation 'androidx.test:core:1.1.0'
    
        androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
        androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0', {
            exclude group: 'com.android.support', module: 'support-annotations'
            exclude group: 'com.android.support', module: 'support-v4'
            exclude group: 'com.android.support', module: 'design'
            exclude group: 'com.android.support', module: 'recyclerview-v7'
        }
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        implementation 'androidx.constraintlayout:constraintlayout-solver:1.1.3'
        androidTestImplementation 'junit:junit:4.13'
        implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
    }
    
    spoon {
        def envSerial = System.env['ANDROID_SERIAL'];
        if (envSerial) {
            devices = [envSerial];
        }
        if (project.hasProperty('spoonClassName')) {
            className = project.spoonClassName
        }
        if (project.hasProperty('spoonMethodName')) {
            methodName = project.spoonMethodName
        }
        debug = true
        adbTimeout = 10*60
    
    //    className = 'com.tcs.integration.smoketests'
    //    methodName = 'testSteeringWheel'
    }

I get this error when gradle syncs:

Cause 1: com.android.build.gradle.internal.crash.ExternalApiUsageException: java.lang.IllegalArgumentException: Cannot change attributes of dependency configuration ':app:debugCompile' after it has been resolved
Caused by: java.lang.IllegalArgumentException: Cannot change attributes of dependency configuration ':app:debugCompile' after it has been resolved
    at org.gradle.api.internal.attributes.ImmutableAttributeContainerWithErrorMessage.attribute(ImmutableAttributeContainerWithErrorMessage.java:57)

Cause 2: org.gradle.api.UnknownDomainObjectException: KotlinJvmAndroidCompilation with name 'debug' not found

2

Answers


  1. Chosen as BEST ANSWER

    I saw messages like these in the gradlew --debug output. RuntimeException: Configuration 'eigen' was resolved during configuration time

    I ignored them at first because they didn't seem related to the exception displayed on the console.

    I created an empty andriod studio project (with the kotlin-andorid plugin) and verified that could "build" without error. I then added parts of our app/build.gradle file into the new project until I encountered the ':app:debugCompileOnly' error.

    The error occurred when I added the extractEigen task. That reminded me of the configuration resolution error I saw in the debug output.

    I fixed the configuration resolution error and that fixed the ':app:debugCompileOnly'error.

    (Fixed by a colleague)


  2. There is no relevant code at all …but according to the error message:

    KotlinJvmAndroidCompilation with name 'debug' not found
    

    I’d suggest to define android.buildTypes.debug, so that it would be known:

    android {
        buildTypes {
            debug {}
        }
    }
    

    But the answer may eventually rather be, to add Crashlytics on class-path (root build.gradle):

    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search