diff options
author | 2025-01-28 14:51:25 -0800 | |
---|---|---|
committer | 2025-01-29 13:54:27 -0800 | |
commit | a610285dfad38fde5a1457955a59769dfe5213ce (patch) | |
tree | 645b053d292af462d41ac5849315dcb47610d773 | |
parent | b3bb1f1c4a4806327a37cb4db2aed571b69420ae (diff) |
Launcher3: Introduce lint checks for Launcher3.
As part of the initial check in, add a test to ensure
that custom Dialogs are not utilized inside of the codebase.
Bug: 389709580
Test: DialogDetectorTest
Flag: NONE Lint checks
Change-Id: I7e3f98c729cdbf4d062419c53a209d12a23b1806
6 files changed, 297 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp index 5b986aba44..1e1e0ad6cc 100644 --- a/Android.bp +++ b/Android.bp @@ -452,6 +452,7 @@ android_app { "AndroidManifest-common.xml", ], lint: { + extra_check_modules: ["Launcher3LintChecker"], baseline_filename: "lint-baseline.xml", }, } diff --git a/checks/Android.bp b/checks/Android.bp new file mode 100644 index 0000000000..dfd701efe0 --- /dev/null +++ b/checks/Android.bp @@ -0,0 +1,46 @@ +// Copyright (C) 2025 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. + +package { + default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_", + default_applicable_licenses: ["Android-Apache-2.0"], +} + +java_library_host { + name: "Launcher3LintChecker", + srcs: ["src/**/*.kt"], + plugins: ["auto_service_plugin"], + libs: [ + "auto_service_annotations", + "lint_api", + ], + kotlincflags: ["-Xjvm-default=all"], +} + +java_test_host { + name: "Launcher3LintCheckerTest", + defaults: ["AndroidLintCheckerTestDefaults"], + srcs: ["tests/**/*.kt"], + data: [ + ":androidx.annotation_annotation", + ":dagger2", + ":kotlinx-coroutines-core", + ], + device_common_data: [ + ":framework", + ], + static_libs: [ + "Launcher3LintChecker", + ], +} diff --git a/checks/src/com/android/internal/launcher3/lint/CustomDialogDetector.kt b/checks/src/com/android/internal/launcher3/lint/CustomDialogDetector.kt new file mode 100644 index 0000000000..37358bbc0b --- /dev/null +++ b/checks/src/com/android/internal/launcher3/lint/CustomDialogDetector.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 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. + */ + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import org.jetbrains.uast.UClass + +/** Detector to identify custom usage of Android's Dialog within the Launcher3 codebase. */ +class CustomDialogDetector : Detector(), SourceCodeScanner { + + override fun applicableSuperClasses(): List<String> { + return listOf(DIALOG_CLASS_NAME) + } + + override fun visitClass(context: JavaContext, declaration: UClass) { + val superTypeClassNames = declaration.superTypes.mapNotNull { it.resolve()?.qualifiedName } + if (superTypeClassNames.contains(DIALOG_CLASS_NAME)) { + context.report( + ISSUE, + declaration, + context.getNameLocation(declaration), + "Class implements Dialog", + ) + } + } + + companion object { + private const val DIALOG_CLASS_NAME = "android.app.Dialog" + + @JvmField + val ISSUE = + Issue.create( + id = "IllegalUseOfCustomDialog", + briefDescription = "dialogs should not be used in Launcher", + explanation = + """ + Don't use custom Dialogs within the launcher code base, instead consider utilizing + AbstractFloatingView to display content that should float above the launcher where + it can be correctly managed for dismissal. + """ + .trimIndent(), + category = Category.CORRECTNESS, + priority = 10, + severity = Severity.ERROR, + implementation = + Implementation(CustomDialogDetector::class.java, Scope.JAVA_FILE_SCOPE), + ) + } +} diff --git a/checks/src/com/android/internal/launcher3/lint/Launcher3IssueRegistry.kt b/checks/src/com/android/internal/launcher3/lint/Launcher3IssueRegistry.kt new file mode 100644 index 0000000000..c77c42bf5d --- /dev/null +++ b/checks/src/com/android/internal/launcher3/lint/Launcher3IssueRegistry.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 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. + */ + +package com.android.internal.launcher3.lint + +import CustomDialogDetector +import com.android.tools.lint.client.api.IssueRegistry +import com.android.tools.lint.client.api.Vendor +import com.android.tools.lint.detector.api.CURRENT_API +import com.android.tools.lint.detector.api.Issue +import com.google.auto.service.AutoService + +@AutoService(IssueRegistry::class) +@Suppress("UnstableApiUsage") +class Launcher3IssueRegistry : IssueRegistry() { + override val issues: List<Issue> + get() = listOf(CustomDialogDetector.ISSUE) + + override val api: Int + get() = CURRENT_API + + override val minApi: Int + get() = 8 + + override val vendor: Vendor = + Vendor( + vendorName = "Android", + feedbackUrl = "http://b/issues/new?component=78010", + contact = "abegovic@google.com", + ) +} diff --git a/checks/tests/com/android/internal/launcher3/lint/CustomDialogDetectorTest.kt b/checks/tests/com/android/internal/launcher3/lint/CustomDialogDetectorTest.kt new file mode 100644 index 0000000000..2a37953038 --- /dev/null +++ b/checks/tests/com/android/internal/launcher3/lint/CustomDialogDetectorTest.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025 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. + */ + +package com.android.internal.launcher3.lint + +import CustomDialogDetector +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test + +/** Test for [CustomDialogDetector]. */ +class CustomDialogDetectorTest : Launcher3LintDetectorTest() { + override fun getDetector(): Detector = CustomDialogDetector() + + override fun getIssues(): List<Issue> = listOf(CustomDialogDetector.ISSUE) + + @Test + fun classDoesNotExtendDialog_noViolation() { + lint() + .files( + TestFiles.kotlin( + """ + package test.pkg + + class SomeClass + """ + .trimIndent() + ), + *androidStubs, + ) + .issues(CustomDialogDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classDoesExtendDialog_violation() { + lint() + .files( + TestFiles.kotlin( + """ + package test.pkg + + import android.app.Dialog + + class SomeClass(context: Context) : Dialog(context) + """ + .trimIndent() + ), + *androidStubs, + ) + .issues(CustomDialogDetector.ISSUE) + .run() + .expect( + (""" + src/test/pkg/SomeClass.kt:5: Error: Class implements Dialog [IllegalUseOfCustomDialog] + class SomeClass(context: Context) : Dialog(context) + ~~~~~~~~~ + 1 errors, 0 warnings + """) + .trimIndent() + ) + } +} diff --git a/checks/tests/com/android/internal/launcher3/lint/Launcher3LintDetectorTest.kt b/checks/tests/com/android/internal/launcher3/lint/Launcher3LintDetectorTest.kt new file mode 100644 index 0000000000..09085c7c95 --- /dev/null +++ b/checks/tests/com/android/internal/launcher3/lint/Launcher3LintDetectorTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 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. + */ + +package com.android.internal.launcher3.lint + +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.checks.infrastructure.TestLintTask +import java.io.File +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** + * Abstract class that should be used by any test for launcher 3 lint detectors. + * + * When you write your test, ensure that you pass [androidStubs] as part of your [TestFiles] + * definition. + */ +@RunWith(JUnit4::class) +abstract class Launcher3LintDetectorTest : LintDetectorTest() { + + /** + * Customize the lint task to disable SDK usage completely. This ensures that running the tests + * in Android Studio has the same result as running the tests in atest + */ + override fun lint(): TestLintTask = + super.lint().allowMissingSdk(true).sdkHome(File("/dev/null")) + + companion object { + private val libraryNames = + arrayOf( + "androidx.annotation_annotation.jar", + "dagger2.jar", + "framework.jar", + "kotlinx-coroutines-core.jar", + ) + + /** + * This file contains stubs of framework APIs and System UI classes for testing purposes + * only. The stubs are not used in the lint detectors themselves. + */ + val androidStubs = + libraryNames + .map { TestFiles.LibraryReferenceTestFile(File(it).canonicalFile) } + .toTypedArray() + } +} |