diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 916ebb792..8e11bb23e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: run: ./gradlew app:lintOrangeDebug - name: Test - run: ./gradlew app:testOrangeDebugUnitTest + run: ./gradlew app:testOrangeDebugUnitTest checks:test - name: Build run: ./gradlew app:buildOrangeDebug diff --git a/app/build.gradle b/app/build.gradle index a4842298a..3e7c1d90a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -217,4 +217,6 @@ dependencies { androidTestImplementation libs.androidx.test.junit androidTestImplementation libs.hilt.android.testing androidTestImplementation libs.androidx.test.core.ktx + + lintChecks project(":checks") } diff --git a/app/src/main/res/layout/activity_account.xml b/app/src/main/res/layout/activity_account.xml index 4f7964a9a..2229ffc78 100644 --- a/app/src/main/res/layout/activity_account.xml +++ b/app/src/main/res/layout/activity_account.xml @@ -427,7 +427,7 @@ - - - + - - @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index 2607d0802..679fb7880 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -13,7 +13,7 @@ app:liftOnScrollTargetViewId="@id/pages" app:layout_collapseMode="pin"> - - - \ No newline at end of file + diff --git a/checks/.gitignore b/checks/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/checks/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/checks/build.gradle b/checks/build.gradle new file mode 100644 index 000000000..b8648c92b --- /dev/null +++ b/checks/build.gradle @@ -0,0 +1,36 @@ +plugins { + id "java-library" + id "kotlin" + id "com.android.lint" +} + +lintOptions { + htmlReport(true) + htmlOutput(file("lint-report.html")) + textReport(true) + absolutePaths(false) + ignoreTestSources(true) +} + +jar { + manifest { + attributes("Lint-Registry-v2": "app.pachli.lint.checks.LintRegistry") + } +} + +dependencies { + compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61") + + // Derived from the AGP version. If the AGP version is X.Y.Z the version + // here must by X+23.Y.Z. + // See https://github.com/googlesamples/android-custom-lint-rules#lint-version + def lintVersion = "31.1.2" // = AGP 8.1.2 + + // For a description of the below dependencies, see the main project README + compileOnly("com.android.tools.lint:lint-api:$lintVersion") + compileOnly("com.android.tools.lint:lint-checks:$lintVersion") + testImplementation("com.android.tools.lint:lint:$lintVersion") + testImplementation("com.android.tools.lint:lint-tests:$lintVersion") + + testImplementation("junit:junit:4.13.2") +} diff --git a/checks/src/main/java/app/pachli/lint/checks/AndroidxToolbarDetector.kt b/checks/src/main/java/app/pachli/lint/checks/AndroidxToolbarDetector.kt new file mode 100644 index 000000000..d5cbce272 --- /dev/null +++ b/checks/src/main/java/app/pachli/lint/checks/AndroidxToolbarDetector.kt @@ -0,0 +1,58 @@ +package app.pachli.lint.checks + +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.LintFix +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.XmlContext +import com.android.tools.lint.detector.api.XmlScanner +import org.w3c.dom.Element + +class AndroidxToolbarDetector : Detector(), XmlScanner { + override fun getApplicableElements(): Collection { + return listOf(ANDROIDX_TOOLBAR) + } + + override fun visitElement(context: XmlContext, element: Element) { + val quickFixData = LintFix.create() + .name("Replace with `com.google.android.material.appbar.MaterialToolbar`") + .replace() + .text(element.localName) + .with("com.google.android.material.appbar.MaterialToolbar") + .independent(true) + .build() + + context.report( + issue = ISSUE, + scope = element, + location = context.getElementLocation(element), + message = "Use `com.google.android.material.appbar.MaterialToolbar` instead of `androidx.appcompat.widget.Toolbar`", + quickfixData = quickFixData, + ) + } + + companion object { + private const val ANDROIDX_TOOLBAR = "androidx.appcompat.widget.Toolbar" + + @JvmField + val ISSUE: Issue = Issue.create( + id = "AndroidxToolbarDetector", + briefDescription = "Don't use `androidx.appcompat.widget.Toolbar` in this project", + explanation = """ + Use `com.google.android.material.appbar.MaterialToolbar` instead of + `androidx.appcompat.widget.Toolbar` to ensure it works as expected with + other Material components. See https://developer.android.com/reference/com/google/android/material/appbar/MaterialToolbar + """, + category = Category.CORRECTNESS, + priority = 6, + severity = Severity.WARNING, + implementation = Implementation( + AndroidxToolbarDetector::class.java, + Scope.RESOURCE_FILE_SCOPE, + ), + ) + } +} diff --git a/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt b/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt new file mode 100644 index 000000000..c0cfde5fc --- /dev/null +++ b/checks/src/main/java/app/pachli/lint/checks/LintRegistry.kt @@ -0,0 +1,14 @@ +package app.pachli.lint.checks + +import com.android.tools.lint.client.api.IssueRegistry +import com.android.tools.lint.detector.api.CURRENT_API +import com.android.tools.lint.detector.api.Issue + +@Suppress("UnstableApiUsage") +class LintRegistry : IssueRegistry() { + override val issues: List + get() = listOf(AndroidxToolbarDetector.ISSUE) + + override val api: Int + get() = CURRENT_API +} diff --git a/checks/src/test/java/app/pachli/lint/checks/AndroidxToolbarDetectorTest.kt b/checks/src/test/java/app/pachli/lint/checks/AndroidxToolbarDetectorTest.kt new file mode 100644 index 000000000..ee540aba0 --- /dev/null +++ b/checks/src/test/java/app/pachli/lint/checks/AndroidxToolbarDetectorTest.kt @@ -0,0 +1,89 @@ +package app.pachli.lint.checks + +import com.android.tools.lint.checks.infrastructure.TestFiles.xml +import com.android.tools.lint.checks.infrastructure.TestLintTask.lint +import org.junit.Test + +class AndroidxToolbarDetectorTest { + @Test + fun testError() { + lint().files( + xml( + "res/layout/test.xml", + """ + + + + + + + + + + """, + ).indented(), + ).issues(AndroidxToolbarDetector.ISSUE).allowMissingSdk().run().expectWarningCount(1) + .expect( + """res/layout/test.xml:16: Warning: Use com.google.android.material.appbar.MaterialToolbar instead of androidx.appcompat.widget.Toolbar [AndroidxToolbarDetector] + + + + + + + + + + + """, + ).indented(), + ).issues(AndroidxToolbarDetector.ISSUE) + .allowMissingSdk() + .run() + .expectWarningCount(0) + } +} diff --git a/settings.gradle b/settings.gradle index fe112d554..4dd3ebaac 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,3 +20,4 @@ enableFeaturePreview("STABLE_CONFIGURATION_CACHE") include ':app' include ':tools:mklanguages' +include ':checks'