Skip to content

Commit 104fc5a

Browse files
authored
Merge pull request #542 from kiwicom/task/exception_to_material_textbutton_lint_rule_for_alert_dialogs
Allow usage of material TextButton inside of Material AlertDialog without lint reporting the usage.
2 parents a12b165 + 01c73c5 commit 104fc5a

File tree

3 files changed

+47
-5
lines changed

3 files changed

+47
-5
lines changed

catalog/src/main/java/kiwi/orbit/compose/catalog/screens/DialogsMaterialDialog.kt

-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package kiwi.orbit.compose.catalog.screens
22

3-
import android.annotation.SuppressLint
43
import androidx.compose.material3.AlertDialog
54
import androidx.compose.runtime.Composable
65
import androidx.navigation.NavController
@@ -13,7 +12,6 @@ internal fun DialogsMaterialDialog(navController: NavController) {
1312
)
1413
}
1514

16-
@SuppressLint("MaterialDesignInsteadOrbitDesign")
1715
@Composable
1816
private fun DialogsMaterialDialog(
1917
onClose: () -> Unit,

lint/src/main/kotlin/kiwi/orbit/compose/lint/detectors/MaterialDesignInsteadOrbitDesignDetector.kt

+36-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,15 @@ import com.android.tools.lint.detector.api.Scope
1010
import com.android.tools.lint.detector.api.Severity
1111
import com.intellij.psi.PsiMember
1212
import com.intellij.psi.impl.source.PsiClassReferenceType
13+
import org.jetbrains.kotlin.psi.KtCallExpression
14+
import org.jetbrains.kotlin.psi.psiUtil.parents
1315
import org.jetbrains.uast.UCallExpression
1416
import org.jetbrains.uast.UElement
1517
import org.jetbrains.uast.UQualifiedReferenceExpression
1618
import org.jetbrains.uast.USimpleNameReferenceExpression
19+
import org.jetbrains.uast.getContainingUClass
20+
import org.jetbrains.uast.toUElement
21+
import org.jetbrains.uast.tryResolveNamed
1722

1823
class MaterialDesignInsteadOrbitDesignDetector : Detector(), Detector.UastScanner {
1924
override fun getApplicableUastTypes(): List<Class<out UElement>> {
@@ -29,9 +34,24 @@ class MaterialDesignInsteadOrbitDesignDetector : Detector(), Detector.UastScanne
2934
override fun visitCallExpression(node: UCallExpression) {
3035
val name = node.methodName ?: return
3136
val wrapperName = node.resolve()?.containingClass?.qualifiedName ?: return
32-
val packageName = wrapperName.substring(0, wrapperName.lastIndexOf("."))
37+
val packageName = getPackageName(wrapperName)
3338
val fqn = "$packageName.$name"
3439
val (preferredName) = METHOD_NAMES.entries.firstOrNull { it.value.contains(fqn) } ?: return
40+
41+
// check the potential violation against our allowlist
42+
val allowedEntry = METHOD_ALLOWLIST_IN_PARENT.entries.find { it.key.contains(fqn) }
43+
if (allowedEntry != null) {
44+
val parentExpression = node.sourcePsi?.parents?.find { it is KtCallExpression }
45+
val resolved = parentExpression.toUElement()?.tryResolveNamed()
46+
val parentName = resolved?.name
47+
val parentWrapper = resolved.toUElement()?.getContainingUClass()?.qualifiedName ?: ""
48+
val parentPackage = getPackageName(parentWrapper)
49+
val parentFqn = "$parentPackage.$parentName"
50+
if (allowedEntry.value.find { parentFqn.contains(it) } != null) {
51+
return
52+
}
53+
}
54+
3555
reportIssue(context, node, "$packageName.$name", preferredName)
3656
}
3757

@@ -66,6 +86,17 @@ class MaterialDesignInsteadOrbitDesignDetector : Detector(), Detector.UastScanne
6686
),
6787
)
6888

89+
private val METHOD_ALLOWLIST_IN_PARENT = mapOf(
90+
"androidx.compose.material.TextButton" to setOf(
91+
"androidx.compose.material.AlertDialog",
92+
"androidx.compose.material3.AlertDialog",
93+
),
94+
"androidx.compose.material3.TextButton" to setOf(
95+
"androidx.compose.material.AlertDialog",
96+
"androidx.compose.material3.AlertDialog",
97+
),
98+
)
99+
69100
private val METHOD_NAMES = mapOf(
70101
"kiwi.orbit.compose.ui.controls.ButtonPrimary" to setOf(
71102
"androidx.compose.material.Button",
@@ -191,5 +222,9 @@ class MaterialDesignInsteadOrbitDesignDetector : Detector(), Detector.UastScanne
191222
"Using $name instead of $preferredName",
192223
)
193224
}
225+
226+
private fun getPackageName(fullyQualifiedName: String): String {
227+
return fullyQualifiedName.substring(0, fullyQualifiedName.lastIndexOf("."))
228+
}
194229
}
195230
}

lint/src/test/kotlin/kiwi/orbit/compose/lint/detectors/MaterialDesignInsteadOrbitDesignDetectorTest.kt

+11-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import java.io.File
1414
import org.junit.Assert
1515
import org.junit.Test
1616

17-
@Suppress("UnstableApiUsage")
1817
class MaterialDesignInsteadOrbitDesignDetectorTest {
1918
@Test
2019
fun testDetector() {
@@ -25,9 +24,11 @@ class MaterialDesignInsteadOrbitDesignDetectorTest {
2524
import androidx.compose.material.contentColorFor
2625
import androidx.compose.material.Text
2726
import androidx.compose.material.icons.Icons
27+
import androidx.compose.material3.AlertDialog
2828
import androidx.compose.material3.Card as Card3
2929
import androidx.compose.material3.contentColorFor as contentColorFor3
3030
import androidx.compose.material3.Text as Text3
31+
import androidx.compose.material3.TextButton
3132
import androidx.compose.material3.Divider
3233
import androidx.compose.material3.LocalTextStyle
3334
fun Test() {
@@ -43,7 +44,9 @@ class MaterialDesignInsteadOrbitDesignDetectorTest {
4344
LocalTextStyle.current
4445
LocalTextStyle
4546
LocalTextStyle.provides()
46-
}
47+
AlertDialog(confirmButton = { TextButton("test") })
48+
}
49+
AlertDialog(confirmButton = { TextButton("test") })
4750
}
4851
""".trimIndent(),
4952
)
@@ -52,6 +55,7 @@ class MaterialDesignInsteadOrbitDesignDetectorTest {
5255
package androidx.compose.material
5356
fun Card(content: () -> Unit) {}
5457
fun Text(content: () -> Unit) {}
58+
fun TextButton(content: String) {}
5559
fun contentColorFor(backgroundColor: Color): Color = TODO()
5660
""".trimIndent(),
5761
)
@@ -60,7 +64,12 @@ class MaterialDesignInsteadOrbitDesignDetectorTest {
6064
package androidx.compose.material3
6165
fun Card(content: () -> Unit) {}
6266
fun Text(content: () -> Unit) {}
67+
fun TextButton(content: String) {}
6368
fun Divider() {}
69+
fun AlertDialog(
70+
confirmButton: () -> Unit,
71+
dismissButton: (() -> Unit)? = null,
72+
) {}
6473
fun contentColorFor(backgroundColor: Color): Color = TODO()
6574
@Stable
6675
sealed class CompositionLocal<T> constructor(defaultFactory: () -> T) {

0 commit comments

Comments
 (0)