Merge "Ensure ActivityResult lint works with newer lint versions" into androidx-master-dev
diff --git a/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt b/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
index 003ed3c..582084e 100644
--- a/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
+++ b/activity/activity-lint/src/main/java/androidx/activity/lint/ActivityResultFragmentVersionDetector.kt
@@ -27,11 +27,15 @@
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.Project
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UElement
import java.util.EnumSet
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.full.memberProperties
+import kotlin.reflect.jvm.isAccessible
class ActivityResultFragmentVersionDetector : Detector(), UastScanner, GradleScanner {
companion object {
@@ -39,7 +43,7 @@
val ISSUE = Issue.create(
id = "InvalidFragmentVersionForActivityResult",
- briefDescription = "Update to $FRAGMENT_VERSION to use ActivityResult APIs",
+ briefDescription = "Update to Fragment $FRAGMENT_VERSION to use ActivityResult APIs",
explanation = """In order to use the ActivityResult APIs you must upgrade your \
Fragment version to $FRAGMENT_VERSION. Previous versions of FragmentActivity \
failed to call super.onRequestPermissionsResult() and used invalid request codes""",
@@ -93,24 +97,79 @@
// always check api dependencies
reportIssue(value, context)
} else if (!checkedImplementationDependencies) {
- val explicitLibraries =
- context.project.currentVariant.mainArtifact.dependencies.libraries
-
- // collect all of the library dependencies
- val allLibraries = HashSet<AndroidLibrary>()
- addIndirectAndroidLibraries(explicitLibraries, allLibraries)
- // check all of the dependencies
- allLibraries.forEach {
- val resolvedCoords = it.resolvedCoordinates
- val groupId = resolvedCoords.groupId
- val artifactId = resolvedCoords.artifactId
- val version = resolvedCoords.version
- reportIssue("$groupId:$artifactId:$version", context, false)
+ val project = context.project
+ if (useNewLintVersion(project)) {
+ checkWithNewLintVersion(project, context)
+ } else {
+ checkWithOldLintVersion(project, context)
}
- checkedImplementationDependencies = true
}
}
+ private fun useNewLintVersion(project: Project): Boolean {
+ project::class.memberFunctions.forEach { function ->
+ if (function.name == "getBuildVariant") {
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun checkWithNewLintVersion(project: Project, context: GradleContext) {
+ val buildVariant = callFunctionWithReflection(project, "getBuildVariant")
+ val mainArtifact = getMemberWithReflection(buildVariant, "mainArtifact")
+ val dependencies = getMemberWithReflection(mainArtifact, "dependencies")
+ val all = callFunctionWithReflection(dependencies, "getAll")
+ (all as ArrayList<*>).forEach { lmLibrary ->
+ lmLibrary::class.memberProperties.forEach { libraryMembers ->
+ if (libraryMembers.name == "resolvedCoordinates") {
+ reportIssue(libraryMembers.call(lmLibrary).toString(), context, false)
+ }
+ }
+ }
+ }
+
+ private fun checkWithOldLintVersion(project: Project, context: GradleContext) {
+ lateinit var explicitLibraries: Collection<AndroidLibrary>
+ val currentVariant = callFunctionWithReflection(project, "getCurrentVariant")
+ val mainArtifact = callFunctionWithReflection(currentVariant, "getMainArtifact")
+ val dependencies = callFunctionWithReflection(mainArtifact, "getDependencies")
+ @Suppress("UNCHECKED_CAST")
+ explicitLibraries =
+ callFunctionWithReflection(dependencies, "getLibraries") as Collection<AndroidLibrary>
+
+ // collect all of the library dependencies
+ val allLibraries = HashSet<AndroidLibrary>()
+ addIndirectAndroidLibraries(explicitLibraries, allLibraries)
+ // check all of the dependencies
+ allLibraries.forEach {
+ val resolvedCoords = it.resolvedCoordinates
+ val groupId = resolvedCoords.groupId
+ val artifactId = resolvedCoords.artifactId
+ val version = resolvedCoords.version
+ reportIssue("$groupId:$artifactId:$version", context, false)
+ }
+ }
+
+ private fun callFunctionWithReflection(caller: Any, functionName: String): Any {
+ caller::class.memberFunctions.forEach { function ->
+ if (function.name == functionName) {
+ function.isAccessible = true
+ return function.call(caller)!!
+ }
+ }
+ return Unit
+ }
+
+ private fun getMemberWithReflection(caller: Any, memberName: String): Any {
+ caller::class.memberProperties.forEach { member ->
+ if (member.name == memberName) {
+ return member.getter.call(caller)!!
+ }
+ }
+ return Unit
+ }
+
private fun addIndirectAndroidLibraries(
libraries: Collection<AndroidLibrary>,
result: MutableSet<AndroidLibrary>
diff --git a/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt b/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
index 88eb0dc..5726acc 100644
--- a/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
+++ b/activity/activity-lint/src/test/java/androidx/activity/lint/ActivityResultFragmentVersionDetectorTest.kt
@@ -146,4 +146,36 @@
""".trimIndent()
)
}
+
+ @Test
+ fun expectFailTransitiveDependency() {
+ val projectFragment = project(kotlin(
+ """
+ package com.example
+
+ import androidx.activity.result.ActivityResultCaller
+ import androidx.activity.result.contract.ActivityResultContract
+
+ val launcher = ActivityResultCaller().registerForActivityResult(ActivityResultContract())
+ """
+ ), gradle(
+ "build.gradle", """
+ dependencies {
+ implementation("androidx.fragment:fragment-ktx:1.3.0-alpha05")
+ }
+ """).indented()).withDependencyGraph("""
+ +--- androidx.fragment:fragment-ktx:1.3.0-alpha05
+ \--- androidx.fragment:fragment:1.3.0-alpha05
+ """.trimIndent()
+ )
+
+ lint().projects(projectFragment).run().expect(
+ """
+ src/main/kotlin/com/example/test.kt:7: Error: Upgrade Fragment version to at least $FRAGMENT_VERSION. [InvalidFragmentVersionForActivityResult]
+ val launcher = ActivityResultCaller().registerForActivityResult(ActivityResultContract())
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 1 errors, 0 warnings
+ """.trimIndent()
+ )
+ }
}