31

When I am trying to build project with value of meta-data tag as a string reference, crashlytics fail with following error:

Crashlytics found an invalid API key: @string/crashlytics. 
Check the Crashlytics plugin to make sure that the application has been added successfully! 
Contact support@crashlytics.com for assistance.

Doesn't work

<meta-data
    android:name="com.crashlytics.ApiKey"
    android:value="@string/crashlytics"/>

Works

<meta-data
    android:name="com.crashlytics.ApiKey"
    android:value="1234567890..."/>

I am want to define different keys inside string.xml for different productFlavors of my android project.

Update

After writing to crashlytics support:

Currently we only are able to evaluate the AndroidManifest.xml at build time so we don't look at any strings resources so we only support a hard coded string. I'll definitely share this with the team that you're interested so we can look into supporting this in a future release.

Dmytro Danylyk
  • 19,684
  • 11
  • 62
  • 68
  • Not easy to do, http://stackoverflow.com/questions/17465353/how-to-replace-a-string-for-a-buildvariant-with-gradle-in-android-studio – Yuraj Sep 06 '14 at 13:31

2 Answers2

53

Edit: The solution accepted is working only if you are using an old version of Crashlytics (I was using v1.1.11). If you are using Fabric SDK you will notice the tasks of the plugin have changed considerably and the script below will not work. Also the API secret is not needed anymore, therefore you can just use the <meta> in the manifest to specify the API key along with a manifest placeholder defined in your flavor:

  • in build.gradle:

    flavor1 {
        ...
        manifestPlaceholders = [crashlyticsApiKey: CRASHLYTICS_API_SECRET_HERE]
        ...
    }
    
  • in AndroidManifest.xml:

    ...
    <meta-data
        android:name="com.crashlytics.ApiKey"
        android:value="${crashlyticsApiKey}" />
    ...
    

There is another undocumented way to specify the Crashlytics key as noted here, and it is to use the crashlytics.properties (in the root of your project) to specify that value along with the API secret:

apiKey=YOUR_API_KEY
apiSecret=YOUR_API_SECRET

Unfortuntately this will not allow you to simply specify a different crashlytics.properties for each flavor, because it needs to be in the root of your project in order to be picked correctly by the gradle plugin. That means you need to generate that file dynamically. The idea is to add the key/secret values in your flavor as custom properties, and generate the crashlytics.properties at buildtime, using the values from the current flavor to fill the file.

The build.gradle inside your android module should look like this:

...
productFlavors {

    flavor1 {
        ...
        set("crashlyticsApiKey", CRASHLYTICS_API_KEY_HERE)
        set("crashlyticsApiSecret", CRASHLYTICS_API_SECRET_HERE)
        ...
    }
    ...
}

File crashlyticsProperties = new File("${project.projectDir.absolutePath}/crashlytics.properties")
applicationVariants.all { variant ->
    variant.productFlavors.each { flavor ->
        def variantSuffix = variant.name.capitalize()
        def generateResourcesTask = project.tasks.getByName("crashlyticsGenerateResources${variantSuffix}")
        def generatePropertiesTask = task("crashlyticsGenerateProperties${variantSuffix}") << {
            Properties properties = new Properties()
            println "...copying apiSecret for ${variant.name}"
            properties.put("apiSecret", flavor.crashlyticsApiSecret)
            println "...copying apiKey for ${variant.name}"
            properties.put("apiKey", flavor.crashlyticsApiKey)
            properties.store(new FileWriter(crashlyticsProperties), "")
        }
        generateResourcesTask.dependsOn generatePropertiesTask
        def cleanResourcesTask = project.tasks.getByName("crashlyticsCleanupResourcesAfterUpload${variantSuffix}")
        cleanResourcesTask.doLast {
            println "...removing crashlytics.properties"
            crashlyticsProperties.delete()
        }
    }
}
...

Basically the script hooks in the building process and generates/fills the properties file just before the Crashlytics gradle plugin does its magic.

a.bertucci
  • 12,142
  • 2
  • 31
  • 32
  • Thanks for your response, but this is huge workaround. I would rather create `AndroidManifest` file for all flavors with different `meta-data`. – Dmytro Danylyk Sep 07 '14 at 08:23
  • 1
    I think you are missing the basic point: of course you can define multiple manifests, but you can define **only the api key in there**. You are forced to put the API secret into the crashlytics.properties, that unfortunately is placed in the root of the project, so you can't define it multiple times. Also please note that the solution I proposed is not a "huge workaround" but is exactly what the guy that works at Crashlytics suggested me to do few months ago. Hopefully they will change their gradle plugin soon, so we will be able to avoid this hell, but until then we have no other options. – a.bertucci Sep 07 '14 at 13:13
  • 1
    Does this still works with the latest version of the plugin? [It didn't work for me.](http://stackoverflow.com/questions/26290067/crashlytics-not-finding-api-key-in-crashlytics-properties-at-runtime?lq=1) – Islam Mustafa Oct 27 '14 at 13:22
  • this is pretty good. but you could just define the crashlytics key and secret in your gradle as globals and use them for all flavors. – Mars Feb 02 '15 at 16:00
  • You're right @Mars, for a simple scenario you can avoid to create the file for each flavor, but this solution is meant to catch a more complex configuration. – a.bertucci Feb 02 '15 at 17:33
  • for those who using Fabric , make sure you change the tasks names to fabric ones i.e : crashlyticsGenerateResources into fabricGenerateResources. though couldn't find any fabricCleanupResourcesAfterUpload task.. any idea why ? – idanakav Apr 28 '15 at 19:51
  • @idaNakav if you are using Fabric then this answer is obsolete. You don't need to hook into the crashlytics tasks because the properties file is not needed anymore (given the secret is not mandatory anymore). – a.bertucci May 02 '15 at 08:17
  • 1
    @idaNakav I've just updated the answer above to inform about the changes in the Fabric SDK and proposing a solution that works with that. – a.bertucci May 02 '15 at 08:47
0

With Fabric's Crashlytics 2.6.6. I'm able to simply select a Build Variant from the menu (usually located on the left in Android Studio) and run the app. It takes a minute or so to propagate through to the Fabric dashboard, but I didn't have any need for a workaround.

marienke
  • 2,465
  • 4
  • 34
  • 66