Skip to content

Conversation

@bingranl
Copy link
Contributor

Fixes #176

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @bingranl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the build logic within the wear module to improve how it consumes the APK generated by the wear:watchface module. It transitions from a manual file system search to a more robust and idiomatic Gradle configuration approach, defining specific configurations for debug and release watchface APKs. This change enhances the reliability and maintainability of the build process for integrating the watchface component.

Highlights

  • Gradle Configuration for Watchface APKs: Introduced dedicated Gradle configurations (watchfaceApkDebug, watchfaceApkRelease) to explicitly declare and resolve the watchface APKs based on their build type, enhancing clarity and robustness.
  • Improved APK Consumption Logic: Refactored the Copy task to leverage these new configurations, allowing Gradle to automatically resolve the watchface APKs instead of manually traversing build directories, simplifying the build process.
  • Enhanced Build System Robustness: By transitioning to Gradle's dependency resolution mechanism, the process of obtaining the watchface APK becomes more reliable and less prone to issues related to build directory structure changes, addressing issue Publish ":wear:watchface" APK and consume it in ":wear" module #176.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the Gradle build logic to consume the watchface APK in a more robust way by using configurations for artifact resolution instead of file system scanning. This is a great improvement. I've identified a few areas where the new code can be made more concise and maintainable by reducing duplication. I also found a potential bug in how the APK file path is resolved in a custom task.

Comment on lines 177 to 178
val apkDirectory = inputFile.singleFile
val apkFile = apkDirectory.resolve("default_watchface.apk")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The inputFile for this task is a ConfigurableFileCollection which, in this case, contains a single APK file. Therefore, inputFile.singleFile returns the File object for the APK itself, not a directory. The variable name apkDirectory is misleading, and calling .resolve() on a file is incorrect. You should directly use the file from inputFile.

        val apkFile = inputFile.singleFile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this right @bingranl ? If so seems a reasonable suggestion

Copy link
Contributor Author

@bingranl bingranl Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inputFile is a FileCollection and the result from singleFile function doesn't have to be a file. In this case, apkDirectory is a directory containing the apk and output-metadata.json.

To improve readability, I manage to wire up the input and output a little differently and believe the new approach looks much better than before

Comment on lines 72 to 96
create("watchfaceApkDebug"){
isCanBeResolved = true
isCanBeConsumed = false

attributes {
attribute(
Attribute.of(com.android.build.api.attributes.BuildTypeAttr::class.java),
objects.named(com.android.build.api.attributes.BuildTypeAttr::class.java, "debug")
)
attribute(Attribute.of("artifactType", String::class.java), "apk")
}
}

create("watchfaceApkRelease") {
isCanBeResolved = true
isCanBeConsumed = false

attributes {
attribute(
Attribute.of(com.android.build.api.attributes.BuildTypeAttr::class.java),
objects.named(com.android.build.api.attributes.BuildTypeAttr::class.java, "release")
)
attribute(Attribute.of("artifactType", String::class.java), "apk")
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's significant duplication in the creation of watchfaceApkDebug and watchfaceApkRelease configurations. You can reduce this by iterating over the build types. This will make the script more maintainable and less error-prone if more build types are added in the future.

    listOf("debug", "release").forEach { buildType ->
        create("watchfaceApk${buildType.replaceFirstChar { it.uppercase() }}") {
            isCanBeResolved = true
            isCanBeConsumed = false

            attributes {
                attribute(
                    Attribute.of(com.android.build.api.attributes.BuildTypeAttr::class.java),
                    objects.named(com.android.build.api.attributes.BuildTypeAttr::class.java, buildType)
                )
                attribute(Attribute.of("artifactType", String::class.java), "apk")
            }
        }
    }

Comment on lines 100 to 101
configurations.getByName("watchfaceApkDebug").dependencies.add(project(":wear:watchface"))
configurations.getByName("watchfaceApkRelease").dependencies.add(project(":wear:watchface"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This dependency declaration can also be simplified to avoid duplication. Using a matching clause is an idiomatic way to apply configuration to a group of configurations.

    configurations.matching { it.name.startsWith("watchfaceApk") }.all { dependencies.add(project(":wear:watchface")) }

Comment on lines 127 to 131
val watchfaceApkConfig = when (variant.name) {
"release" -> configurations.getByName("watchfaceApkRelease")
"debug" -> configurations.getByName("watchfaceApkDebug")
else -> throw RuntimeException("Cannot find watchface apk configuration")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This when statement can be simplified by constructing the configuration name dynamically from the variant.name. This makes the code more concise and less prone to errors if new variants are added, as it removes the need to update this block manually.

    val watchfaceApkConfig = configurations.getByName("watchfaceApk$capsVariant")

isCanBeResolved = true
}

create("watchfaceApkDebug"){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a single configuration that doesn't specify com.android.build.api.attributes.BuildTypeAttr::class.java and we just set that in artifactView at the consumption place. That way you don't accidentally add the project dependency just to a single configuration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also should use register instead of create. Configurations are lazy starting 9.1.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions! I tried adding build attributes and use artifactView on the consumption side(in the copy task). However, it didn't work for me and gradle keeps telling me "consumer didn't ask for build type attribute and it cannot choose between debugRuntimeElements and releaseRuntimeElements. I am quite confused.

I didn't some research and it seems the reason is "attributes on the artifactView only refine what artifacts are retrieved after the initial variant resolution has occurred. They don't provide the necessary attributes to guide the initial variant selection for the entire configuration"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today I learn. Thanks for sharing!


attributes {
attribute(
Attribute.of(com.android.build.api.attributes.BuildTypeAttr::class.java),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use import com.android.build.api.attributes.BuildTypeAttr to make this easier to read.

}

dependencies {
configurations.getByName("watchfaceApkDebug").dependencies.add(project(":wear:watchface"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: here you can use

dependencies {
    "watchfaceApkDebug"(project(":wear:watchface"))
}

this syntax as it is easier to read.

@riggaroo riggaroo requested a review from garanj November 17, 2025 10:23
@garanj
Copy link
Contributor

garanj commented Nov 17, 2025

Thanks @bingranl - works great! Thanks so much for the Gradle improvements, really appreciate this opportunity to learn! Approving from the functionality perspective, looks like @liutikas and gemini-code-assist had some worthwhile minor revisions.

@bingranl
Copy link
Contributor Author

All the comments are addressed. Hope this change would help our "AB" efforts

@riggaroo riggaroo merged commit b4ffd38 into android:main Nov 24, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish ":wear:watchface" APK and consume it in ":wear" module

4 participants