I think we’re all annoyed with this problem: when using Google’s Android libraries (for example, material for compose) you open the source code of some compose function and see this:

Unresolved sample source code 😢

When I upgraded to compose 1.7.0-beta02 (Android Studio Koala 2024.1.1 RC 1), I was pleasantly surprised: the sample’s source code is now being correctly resolved and displayed!

Resolved sample source code 😎

I immediately started 🕵️ investigating how this feature works and what Google did to correctly display the source code of their samples in Android Studio, because I wanted to add this nice feature to the design system library I’m developing.

Basically, the requirement is that you simply need to have the declarations mentioned in @sample tags to be present in the sources.jar file — a file that is distributed along with .pom, .module, .aar, and other files through maven.

As it turns out, there is a very easy way to do this: just add this to build.gradle.kts of the library module that you’re publishing to maven:

tasks.withType<SourceJarTask>() {
from(file("$rootDir/samples/src/main/kotlin"))
}

assuming that all the samples are placed in the :samples gradle module

That’s it!

Since I first noticed working samples in Google’s material for compose library and didn’t yet know how to replicate it, I searched in the AndroidX repo’s build files for answers.

Here’s what I found:

Many (if not all) of AndroidX libs are published with 2 sources.jar files:

Source
{
"name": "sourcesElements",
"attributes": {
"org.gradle.category": "documentation",
"org.gradle.dependency.bundling": "external",
"org.gradle.docstype": "sources",
"org.gradle.usage": "java-runtime"
},
"files": [
{
"name": "haptics-1.0.0-alpha01-sources.jar"
// ...
},
{
"name": "haptics-1.0.0-alpha01-samples-sources.jar"
// ...
}
]
}

Source

One is sources.jar and the other is samples-sources.jar. How they achieve this seems non-trivial. Here are some links that seem relevant to this logic: one, two, three.

The solution I suggested at the beginning of this article is different from Google’s because it packages sample sources together with the library’s sources into one sources.jar. Seems like there’s not much difference between these two approaches except the first one is simpler to implement.

However, it is up to you to make sure that sample sources do not have the same package name and simple name as some of the declarations in the main library, as it can cause a name collision. I suggest using a unique package name for all sources that is not used anywhere else in the main library.

Compose Android plugin for Android Studio contains support for resolving KDoc links to sample code that is not present in the java byte code, but present in the sources.jar. Here it is.

If you add your :samples module as a dependency to the demo app module, all samples will compile every time the demo app is launched, ensuring that samples will always remain up-to-date, as opposed to just directly writing Kotlin code in KDoc.

Specifically in the context of creating samples for design system libraries, I would like to recommend adding images to KDoc of samples.

If you are already using screenshot testing in your design system, it’s very easy to upload these screenshots to something like GitHub/GitLab Pages and link to them in the KDoc of the composable functions.

Like in the material library:

Rendered image of the Button composable in its KDoc in the IDE

BTW, to see images in KDoc, you can:

  1. Wait for this issue in the IntelliJ IDEA to be resolved
  2. Install the Kelp plugin 🔌

You can do the same with your samples: just add screenshot testing to your :samples module, upload them to the web on CI, and add links to the pictures in KDoc of samples. This way, users can see what is the result of running any sample code without actually running it. This increases developer productivity, especially when the design system is new to users.

Happy coding! 👋

Source link