Skip to content

Instantly share code, notes, and snippets.

internal class StyleAttrUsesThemeDetector : ResourceXmlDetector() {
companion object {
val ISSUE = createErrorThemesAndStylesIssue(StyleAttrUsesThemeDetector::class, Scope.RESOURCE_FILE_SCOPE)
}
override fun appliesTo(folderType: ResourceFolderType) = folderType == ResourceFolderType.LAYOUT
override fun getApplicableAttributes(): Collection<String> = XmlScannerConstants.ALL
internal fun createErrorThemesAndStylesIssue(detectorClass: KClass<out Detector>, scope: EnumSet<Scope>): Issue {
return createThemesAndStylesIssue(detectorClass, Severity.ERROR, scope)
}
internal fun createWarningThemesAndStylesIssue(detectorClass: KClass<out Detector>, scope: EnumSet<Scope>): Issue {
return createThemesAndStylesIssue(detectorClass, Severity.WARNING, scope)
}
private fun createThemesAndStylesIssue(
detectorClass: KClass<out Detector>,
internal fun Attr.belongsToItem() = ownerElement.nodeName == "item"
internal fun Element.belongsToStyle() = parentNode.nodeName == "style"
internal fun Element.belongsToThemeOrThemeOverlay() = belongsToStyle()
&& parentNode.attributes.getNamedItem("name").nodeValue.startsWith("Theme")
/**
* Looks for VALUE in a <style> resource
* e.g. <item name="attrName">VALUE</item>
/**
* Prohibits use of hardcoded colors in XML layouts and color resources.
*
* A hardcoded color includes:
*
* - a reference to a color resource which doesn't include "mds" in the name.
* "mds" is used as an allowlist filter, where we'll assume that the resource contains theme-friendly colors
* or it's an exception to the rule.
* - a color hexcode
*
override val issues: List<Issue> = listOf(
MaterialThemeOverlayInLayoutDetector.ISSUE,
MonzoToolbarAttributeDetectors.ISSUE_LAYOUT_HEIGHT,
MonzoToolbarAttributeDetectors.ISSUE_MIN_HEIGHT,
MonzoToolbarAttributeDetectors.ISSUE_STYLE,
MonzoToolbarNotUsedDetector.ISSUE,
StatusBarAttrsDetector.ISSUE,
StyleAttrUsesTextAppearanceDetector.ISSUE,
StyleAttrUsesThemeDetector.ISSUE,
SwitchMaterialNotUsedDetector.ISSUE,
class RatingsView(context: Context, attrs: AttributeSet)
: View(context, attrs) {
private val accessHelper = AccessHelper()
init {
ViewCompat.setAccessibilityDelegate(this, accessHelper)
}
// override fun onDraw(canvas: Canvas)
override fun getVisibleVirtualViews(virtualViewIds: MutableList<Int>) {
val numberOfIntervals = ratingsHistogram?.intervals?.size ?: 0
for (i in 0 until numberOfIntervals) {
virtualViewIds.add(i)
}
}
override fun getVirtualViewAt(x: Float, y: Float): Int {
ratingsHistogram?.let {
val intervalWidth = width.toFloat() / it.intervals.size
return min((x / intervalWidth).toInt(), it.intervals.size - 1)
}
return HOST_ID
}
private val intervalBounds = Rect()
@Suppress("DEPRECATION") // setBoundsInParent is required by [ExploreByTouchHelper]
override fun onPopulateNodeForVirtualView(virtualViewId: Int, node: AccessibilityNodeInfoCompat) {
node.className = RatingsHistogramView::class.simpleName
node.contentDescription = "content desc $virtualViewId"
updateBoundsForInterval(virtualViewId)
node.setBoundsInParent(intervalBounds)
}
override fun dispatchHoverEvent(event: MotionEvent): Boolean {
return if (accessHelper.dispatchHoverEvent(event)) {
true
} else {
super.dispatchHoverEvent(event)
}
}