Clean up TargetApiAnnotationUsageDetector, add lint fix
Fixes suppression in slice-view library and compose test-utils.
Fixes: 187503648
Test: TargetApiAnnotationUsageDetectorTest
Change-Id: I09189013af457f23691f55aa74076bef6a5075db
diff --git a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
index fe28c00..2275c67 100644
--- a/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
+++ b/compose/test-utils/src/androidMain/kotlin/androidx/compose/testutils/AndroidComposeTestCaseRunner.android.kt
@@ -16,7 +16,6 @@
package androidx.compose.testutils
-import android.annotation.TargetApi
import android.app.Activity
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -28,6 +27,7 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
+import androidx.annotation.RequiresApi
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionContext
@@ -79,6 +79,8 @@
private val screenWithSpec: Int
private val screenHeightSpec: Int
+
+ @Suppress("NewApi") // NewApi doesn't understand Kotlin `when` (b/189459502)
private val capture = when {
supportsRenderNode -> RenderNodeCapture()
supportsMRenderNode -> MRenderNodeCapture()
@@ -342,7 +344,7 @@
fun endRecording()
}
-@TargetApi(Build.VERSION_CODES.Q)
+@RequiresApi(Build.VERSION_CODES.Q)
private class RenderNodeCapture : DrawCapture {
private val renderNode = RenderNode("Test")
diff --git a/fragment/integration-tests/testapp/lint-baseline.xml b/fragment/integration-tests/testapp/lint-baseline.xml
deleted file mode 100644
index 64d6c6c..0000000
--- a/fragment/integration-tests/testapp/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.0.0-alpha15" type="baseline" client="cli" name="Lint" variant="all" version="7.0.0-alpha15">
-
- <issue
- id="BanTargetApiAnnotation"
- message="Uses @TargetApi annotation"
- errorLine1="@TargetApi(Build.VERSION_CODES.LOLLIPOP)"
- errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt"
- line="31"
- column="1"/>
- </issue>
-
-</issues>
diff --git a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt
index ee8f55d..3d1826c 100644
--- a/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt
+++ b/fragment/integration-tests/testapp/src/main/java/androidx/fragment/testapp/kittenfragmenttransitions/DetailsTransition.kt
@@ -15,10 +15,10 @@
*/
package androidx.fragment.testapp.kittenfragmenttransitions
-import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
+import androidx.annotation.RequiresApi
import androidx.transition.ChangeBounds
import androidx.transition.ChangeImageTransform
import androidx.transition.ChangeTransform
@@ -28,7 +28,7 @@
* Transition that performs almost exactly like [android.transition.AutoTransition], but has
* an added [ChangeImageTransform] to support properly scaling up our gorgeous kittens.
*/
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class DetailsTransition : TransitionSet {
constructor() {
init()
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 60769c1..6f93b40 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -36,7 +36,7 @@
BanConcurrentHashMap.ISSUE,
BanInappropriateExperimentalUsage.ISSUE,
BanKeepAnnotation.ISSUE,
- BanTargetApiAnnotation.ISSUE,
+ TargetApiAnnotationUsageDetector.ISSUE,
SampledAnnotationEnforcer.MISSING_SAMPLED_ANNOTATION,
SampledAnnotationEnforcer.OBSOLETE_SAMPLED_ANNOTATION,
SampledAnnotationEnforcer.MISSING_SAMPLES_DIRECTORY,
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanTargetApiAnnotation.kt b/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
similarity index 63%
rename from lint-checks/src/main/java/androidx/build/lint/BanTargetApiAnnotation.kt
rename to lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
index b6e8e0b..4e9fd59 100644
--- a/lint-checks/src/main/java/androidx/build/lint/BanTargetApiAnnotation.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/TargetApiAnnotationUsageDetector.kt
@@ -29,7 +29,10 @@
import com.android.tools.lint.detector.api.Severity
import org.jetbrains.uast.UAnnotation
-class BanTargetApiAnnotation : Detector(), Detector.UastScanner {
+/**
+ * Enforces policy banning use of the `@TargetApi` annotation.
+ */
+class TargetApiAnnotationUsageDetector : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes() = listOf(UAnnotation::class.java)
@@ -42,7 +45,14 @@
if (node.qualifiedName == "android.annotation.TargetApi") {
context.report(
ISSUE, node, context.getNameLocation(node),
- "Uses @TargetApi annotation"
+ "Use `@RequiresApi` instead of `@TargetApi`",
+ fix().name("Replace with `@RequiresApi`")
+ .replace()
+ .pattern("(?:android\\.annotation\\.)?TargetApi")
+ .with("androidx.annotation.RequiresApi")
+ .shortenNames()
+ .autoFix(true, true)
+ .build(),
)
}
}
@@ -51,11 +61,13 @@
companion object {
val ISSUE = Issue.create(
"BanTargetApiAnnotation",
- "Uses @TargetApi annotation",
- "Use of @TargetApi annotation is not allowed, please consider " +
- "using the @RequiresApi annotation instead.",
+ "Replace usage of `@TargetApi` with `@RequiresApi`",
+ "The `@TargetApi` annotation satisfies the `NewApi` lint check, but it does " +
+ "not ensure that calls to the annotated API are correctly guarded on an `SDK_INT`" +
+ " (or equivalent) check. Instead, use the `@RequiresApi` annotation to ensure " +
+ "that all calls are correctly guarded.",
Category.CORRECTNESS, 5, Severity.ERROR,
- Implementation(BanTargetApiAnnotation::class.java, Scope.JAVA_FILE_SCOPE)
+ Implementation(TargetApiAnnotationUsageDetector::class.java, Scope.JAVA_FILE_SCOPE)
)
}
}
diff --git a/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
similarity index 75%
rename from lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
rename to lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
index 49429e0..5ccf192 100644
--- a/lint-checks/src/test/java/androidx/build/lint/BanTargetApiAnnotationTest.kt
+++ b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationDetectorTest.kt
@@ -23,9 +23,9 @@
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
-class BanTargetApiAnnotationTest : AbstractLintDetectorTest(
- useDetector = BanTargetApiAnnotation(),
- useIssues = listOf(BanTargetApiAnnotation.ISSUE),
+class TargetApiAnnotationDetectorTest : AbstractLintDetectorTest(
+ useDetector = TargetApiAnnotationUsageDetector(),
+ useIssues = listOf(TargetApiAnnotationUsageDetector.ISSUE),
) {
@Test
@@ -36,10 +36,10 @@
/* ktlint-disable max-line-length */
val expected = """
-src/androidx/TargetApiUsageJava.java:22: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+src/androidx/TargetApiUsageJava.java:22: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(29)
~~~~~~~~~~~~~~
-src/androidx/TargetApiUsageJava.java:25: Error: Uses @TargetApi annotation [BanTargetApiAnnotation]
+src/androidx/TargetApiUsageJava.java:25: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
@TargetApi(30)
~~~~~~~~~~~~~~
2 errors, 0 warnings
diff --git a/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt
new file mode 100644
index 0000000..5bf4605
--- /dev/null
+++ b/lint-checks/src/test/java/androidx/build/lint/TargetApiAnnotationUsageDetectorTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+package androidx.build.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintResult
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+import org.junit.Test
+
+class TargetApiAnnotationUsageDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = TargetApiAnnotationUsageDetector()
+
+ override fun getIssues(): List<Issue> = listOf(
+ TargetApiAnnotationUsageDetector.ISSUE
+ )
+
+ private fun check(testFile: TestFile): TestLintResult {
+ return lint().files(
+ java(annotationSource),
+ testFile
+ ).run()
+ }
+
+ private val annotationSource = """
+package android.annotation;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({TYPE, METHOD, CONSTRUCTOR})
+@Retention(RetentionPolicy.CLASS)
+public @interface TargetApi {
+ int value();
+}
+ """.trimIndent()
+
+ @Test
+ fun testAnnotationUsageJava() {
+ val input = java(
+ """
+package androidx.sample;
+
+import android.annotation.TargetApi;
+
+@TargetApi(24)
+public class SampleClass {
+ @TargetApi(15)
+ public void method() {
+ // Stub
+ }
+}
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/sample/SampleClass.java:5: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
+@TargetApi(24)
+~~~~~~~~~~~~~~
+src/androidx/sample/SampleClass.java:7: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
+ @TargetApi(15)
+ ~~~~~~~~~~~~~~
+2 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ /* ktlint-disable max-line-length */
+ val expectFixDiffs = """
+Fix for src/androidx/sample/SampleClass.java line 5: Replace with `@RequiresApi`:
+@@ -5 +5
+- @TargetApi(24)
++ @androidx.annotation.RequiresApi(24)
+Fix for src/androidx/sample/SampleClass.java line 7: Replace with `@RequiresApi`:
+@@ -7 +7
+- @TargetApi(15)
++ @androidx.annotation.RequiresApi(15)
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(input)
+ .expect(expected)
+ .expectFixDiffs(expectFixDiffs)
+ }
+
+ @Test
+ fun testAnnotationUsageKt() {
+ val input = kotlin(
+ """
+package androidx.sample
+
+import android.annotation.TargetApi
+
+@TargetApi(24)
+class SampleClass {
+ @TargetApi(15)
+ fun method() {
+ // Stub
+ }
+}
+ """.trimIndent()
+ )
+
+ /* ktlint-disable max-line-length */
+ val expected = """
+src/androidx/sample/SampleClass.kt:5: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
+@TargetApi(24)
+~~~~~~~~~~~~~~
+src/androidx/sample/SampleClass.kt:7: Error: Use @RequiresApi instead of @TargetApi [BanTargetApiAnnotation]
+ @TargetApi(15)
+ ~~~~~~~~~~~~~~
+2 errors, 0 warnings
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ /* ktlint-disable max-line-length */
+ val expectFixDiffs = """
+Fix for src/androidx/sample/SampleClass.kt line 5: Replace with `@RequiresApi`:
+@@ -5 +5
+- @TargetApi(24)
++ @androidx.annotation.RequiresApi(24)
+Fix for src/androidx/sample/SampleClass.kt line 7: Replace with `@RequiresApi`:
+@@ -7 +7
+- @TargetApi(15)
++ @androidx.annotation.RequiresApi(15)
+ """.trimIndent()
+ /* ktlint-enable max-line-length */
+
+ check(input)
+ .expect(expected)
+ .expectFixDiffs(expectFixDiffs)
+ }
+}
diff --git a/slices/view/lint-baseline.xml b/slices/view/lint-baseline.xml
index 3ee1112..6642ebe 100644
--- a/slices/view/lint-baseline.xml
+++ b/slices/view/lint-baseline.xml
@@ -13,17 +13,6 @@
</issue>
<issue
- id="BanTargetApiAnnotation"
- message="Uses @TargetApi annotation"
- errorLine1=" @TargetApi(Build.VERSION_CODES.LOLLIPOP)"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="src/main/java/androidx/slice/widget/LocationBasedViewTracker.java"
- line="47"
- column="5"/>
- </issue>
-
- <issue
id="ClassVerificationFailure"
message="This call references a method added in API level 20; however, the containing class androidx.slice.widget.ActionRow is reachable from earlier API levels and will fail run-time class verification."
errorLine1=" if (input.getRemoteInput().getAllowFreeFormInput()) {"
diff --git a/slices/view/src/main/java/androidx/slice/widget/LocationBasedViewTracker.java b/slices/view/src/main/java/androidx/slice/widget/LocationBasedViewTracker.java
index a35b944..b1b3c18 100644
--- a/slices/view/src/main/java/androidx/slice/widget/LocationBasedViewTracker.java
+++ b/slices/view/src/main/java/androidx/slice/widget/LocationBasedViewTracker.java
@@ -18,7 +18,6 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
-import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
@@ -26,6 +25,7 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import java.util.ArrayList;
@@ -44,7 +44,7 @@
}
};
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private static final SelectionLogic A11Y_FOCUS = new SelectionLogic() {
@Override
public void selectView(View view) {