mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
add lint rule for OnPreDrawListener
commit_hash:d17e85be9d08761e6e79c11101cc527bfb837859
This commit is contained in:
+11
-4
@@ -624,6 +624,9 @@
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/images/DivImagePriority.kt":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/images/DivImagePriority.kt",
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/images/LoadReference.java":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/images/LoadReference.java",
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/uri/UriHandler.java":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/uri/UriHandler.java",
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/view/DrawingPassOverrideStrategy.kt":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/view/DrawingPassOverrideStrategy.kt",
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/view/OnPreDrawListeners.kt":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/view/OnPreDrawListeners.kt",
|
||||
"client/android/div-core/src/main/java/com/yandex/div/core/view/SafeDrawingPassOverrideStrategy.kt":"divkit/public/client/android/div-core/src/main/java/com/yandex/div/core/view/SafeDrawingPassOverrideStrategy.kt",
|
||||
"client/android/div-data/build.gradle":"divkit/public/client/android/div-data/build.gradle",
|
||||
"client/android/div-data/div2-generator-config.json":"divkit/public/client/android/div-data/div2-generator-config.json",
|
||||
"client/android/div-data/div2-shared-data-generator-config.json":"divkit/public/client/android/div-data/div2-shared-data-generator-config.json",
|
||||
@@ -1174,9 +1177,9 @@
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/ImageRepresentation.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/ImageRepresentation.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/ImageUtils.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/ImageUtils.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/Releasables.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/Releasables.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/ReportingSafeDrawingPassOverrideStrategy.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/ReportingSafeDrawingPassOverrideStrategy.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SafeAlertDialog.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SafeAlertDialog.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SafeAlertDialogBuilder.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SafeAlertDialogBuilder.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SafeDrawingPassOverrideStrategy.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SafeDrawingPassOverrideStrategy.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SafePopupWindow.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SafePopupWindow.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SearchUtil.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SearchUtil.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/util/SparseArrays.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/util/SparseArrays.kt",
|
||||
@@ -1401,13 +1404,11 @@
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/DivExtendableView.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/DivExtendableView.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/DivViewDelegate.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/DivViewDelegate.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/DivViewWrapper.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/DivViewWrapper.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/DrawingPassOverrideStrategy.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/DrawingPassOverrideStrategy.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/FixedLineHeightHelper.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/FixedLineHeightHelper.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/FixedLineHeightView.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/FixedLineHeightView.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/GridContainer.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/GridContainer.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/LinearContainerLayout.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/LinearContainerLayout.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/LoadableImageView.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/LoadableImageView.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/OverridableOnPreDrawListener.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/OverridableOnPreDrawListener.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/ShowSeparatorsMode.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/ShowSeparatorsMode.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/ViewPager2Wrapper.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/ViewPager2Wrapper.kt",
|
||||
"client/android/div/src/main/java/com/yandex/div/core/widget/Views.kt":"divkit/public/client/android/div/src/main/java/com/yandex/div/core/widget/Views.kt",
|
||||
@@ -1557,7 +1558,7 @@
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/DivPatchApplyItemsTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/DivPatchApplyItemsTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/DivWalkTreeTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/EnableAssertsRule.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/EnableAssertsRule.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/SafeDrawingPassOverrideStrategyTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/SafeDrawingPassOverrideStrategyTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/ReportingSafeDrawingPassOverrideStrategyTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/ReportingSafeDrawingPassOverrideStrategyTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/TypeConverterTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/TypeConverterTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/mask/CurrencyMaskTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/mask/CurrencyMaskTest.kt",
|
||||
"client/android/div/src/test/java/com/yandex/div/core/util/mask/FixedLengthMaskTest.kt":"divkit/public/client/android/div/src/test/java/com/yandex/div/core/util/mask/FixedLengthMaskTest.kt",
|
||||
@@ -12155,6 +12156,12 @@
|
||||
"client/android/gradle/wrapper/gradle-wrapper.properties":"divkit/public/client/android/gradle/wrapper/gradle-wrapper.properties",
|
||||
"client/android/gradlew":"divkit/public/client/android/gradlew",
|
||||
"client/android/gradlew.bat":"divkit/public/client/android/gradlew.bat",
|
||||
"client/android/lint-rules/build.gradle":"divkit/public/client/android/lint-rules/build.gradle",
|
||||
"client/android/lint-rules/src/main/java/com/yandex/div/lint/DivKitIssueRegistry.kt":"divkit/public/client/android/lint-rules/src/main/java/com/yandex/div/lint/DivKitIssueRegistry.kt",
|
||||
"client/android/lint-rules/src/main/java/com/yandex/div/lint/OnPreDrawListenerDetector.kt":"divkit/public/client/android/lint-rules/src/main/java/com/yandex/div/lint/OnPreDrawListenerDetector.kt",
|
||||
"client/android/lint-rules/src/main/java/com/yandex/div/lint/OnPreDrawListenerIssue.kt":"divkit/public/client/android/lint-rules/src/main/java/com/yandex/div/lint/OnPreDrawListenerIssue.kt",
|
||||
"client/android/lint-rules/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry":"divkit/public/client/android/lint-rules/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry",
|
||||
"client/android/lint-rules/src/test/java/com/yandex/div/lint/OnPreDrawListenerDetectorTest.kt":"divkit/public/client/android/lint-rules/src/test/java/com/yandex/div/lint/OnPreDrawListenerDetectorTest.kt",
|
||||
"client/android/logging/build.gradle":"divkit/public/client/android/logging/build.gradle",
|
||||
"client/android/logging/jacoco.excludes":"divkit/public/client/android/logging/jacoco.excludes",
|
||||
"client/android/logging/proguard-rules.pro":"divkit/public/client/android/logging/proguard-rules.pro",
|
||||
|
||||
@@ -126,6 +126,7 @@ apiValidation {
|
||||
"divkit-demo-app",
|
||||
"divkit-perftests",
|
||||
"divkit-regression-testing",
|
||||
"lint-rules",
|
||||
"sample",
|
||||
"screenshot-test-runtime",
|
||||
"ui-test-common",
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.yandex.div.core.view
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
|
||||
@InternalApi
|
||||
public fun interface DrawingPassOverrideStrategy {
|
||||
|
||||
public fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean): Boolean
|
||||
|
||||
@InternalApi
|
||||
public object NoOp : DrawingPassOverrideStrategy {
|
||||
override fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean): Boolean = proceed
|
||||
}
|
||||
|
||||
@InternalApi
|
||||
public object Safe : SafeDrawingPassOverrideStrategy()
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
@file:JvmName("OnPreDrawListeners")
|
||||
|
||||
package com.yandex.div.core.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewTreeObserver
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
|
||||
@InternalApi
|
||||
public fun onPreDrawListener(
|
||||
overrideStrategy: DrawingPassOverrideStrategy = DrawingPassOverrideStrategy.Safe,
|
||||
action: () -> Boolean
|
||||
): ViewTreeObserver.OnPreDrawListener {
|
||||
return OverridableOnPreDrawListener(
|
||||
delegate = action,
|
||||
overrideStrategy
|
||||
)
|
||||
}
|
||||
|
||||
@InternalApi
|
||||
public fun onPreDrawListener(
|
||||
overrideStrategy: DrawingPassOverrideStrategy = DrawingPassOverrideStrategy.Safe,
|
||||
delegate: ViewTreeObserver.OnPreDrawListener
|
||||
): ViewTreeObserver.OnPreDrawListener {
|
||||
return OverridableOnPreDrawListener(
|
||||
delegate = delegate,
|
||||
overrideStrategy
|
||||
)
|
||||
}
|
||||
|
||||
@InternalApi
|
||||
@SuppressLint("OnPreDrawListenerIssue")
|
||||
public class OverridableOnPreDrawListener @JvmOverloads constructor(
|
||||
private val delegate: ViewTreeObserver.OnPreDrawListener,
|
||||
private val overrideStrategy: DrawingPassOverrideStrategy = DrawingPassOverrideStrategy.Safe
|
||||
) : ViewTreeObserver.OnPreDrawListener {
|
||||
|
||||
override fun onPreDraw(): Boolean {
|
||||
val proceed = delegate.onPreDraw()
|
||||
return overrideStrategy.overrideDrawingPass(delegate, proceed)
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.yandex.div.core.view
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
import com.yandex.div.core.annotations.InternalApi
|
||||
|
||||
@InternalApi
|
||||
public open class SafeDrawingPassOverrideStrategy : DrawingPassOverrideStrategy {
|
||||
|
||||
public var frameCancelLimit: Int = DEFAULT_FRAME_CANCEL_LIMIT
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
frameCancelCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
private var frameCancelCount = 0
|
||||
|
||||
override fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean): Boolean {
|
||||
if (proceed) {
|
||||
frameCancelCount = 0
|
||||
return true
|
||||
} else if (frameCancelCount < frameCancelLimit) {
|
||||
frameCancelCount++
|
||||
onFrameCancelled(listener, frameCancelCount)
|
||||
return false
|
||||
} else if (frameCancelCount == frameCancelLimit) {
|
||||
frameCancelCount++
|
||||
onFrameCancelLimitExceeded(listener, frameCancelCount)
|
||||
return true
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onFrameCancelled(listener: OnPreDrawListener, frameCancelCount: Int): Unit = Unit
|
||||
|
||||
protected open fun onFrameCancelLimitExceeded(listener: OnPreDrawListener, frameCancelCount: Int): Unit = Unit
|
||||
|
||||
private companion object {
|
||||
const val DEFAULT_FRAME_CANCEL_LIMIT: Int = 3
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: "kotlin-allopen"
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
id 'org.jetbrains.kotlin.plugin.allopen'
|
||||
}
|
||||
|
||||
apply from: "${project.projectDir}/../publish-java.gradle"
|
||||
apply from: "${project.projectDir}/../div-tests-coverage.gradle"
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
apply from: "${buildscript.sourceFile.parent}/div-common.gradle"
|
||||
|
||||
dependencies {
|
||||
lintChecks project(path: ':lint-rules')
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ package com.yandex.div.sizeprovider
|
||||
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import com.yandex.div.core.extension.DivExtensionHandler
|
||||
import com.yandex.div.core.view.onPreDrawListener
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.divs.pxToDp
|
||||
import com.yandex.div.json.expressions.ExpressionResolver
|
||||
@@ -59,7 +59,7 @@ class DivSizeProviderExtensionHandler(
|
||||
|
||||
if (divView.getTag(R.id.div_size_provider_clear_variables_listener) != null) return
|
||||
|
||||
val clearVariablesListener = ViewTreeObserver.OnPreDrawListener {
|
||||
val clearVariablesListener = onPreDrawListener {
|
||||
variablesHolder.clear()
|
||||
sizes.forEach { divView.setVariable(it.key, it.value.toString()) }
|
||||
sizes.clear()
|
||||
|
||||
@@ -2,7 +2,6 @@ package com.yandex.div.core.dagger
|
||||
|
||||
import com.yandex.div.core.expression.local.DivRuntimeVisitor
|
||||
import com.yandex.div.core.tooltip.DivTooltipController
|
||||
import com.yandex.div.core.util.SafeDrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.DivTransitionBuilder
|
||||
import com.yandex.div.core.view2.DivViewIdProvider
|
||||
@@ -15,6 +14,7 @@ import com.yandex.div.core.view2.errors.ErrorVisualMonitor
|
||||
import com.yandex.div.core.view2.reuse.InputFocusTracker
|
||||
import com.yandex.div.core.view2.state.DivStateSwitcher
|
||||
import com.yandex.div.core.view2.state.DivStateTransitionHolder
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
import com.yandex.yatagan.BindsInstance
|
||||
import com.yandex.yatagan.Component
|
||||
|
||||
@@ -40,7 +40,7 @@ internal interface Div2ViewComponent {
|
||||
val animatorController: DivAnimatorController
|
||||
val divTooltipController: DivTooltipController
|
||||
val runtimeVisitor: DivRuntimeVisitor
|
||||
val drawingPassOverrideStrategy: SafeDrawingPassOverrideStrategy
|
||||
val drawingPassOverrideStrategy: DrawingPassOverrideStrategy
|
||||
|
||||
@Component.Builder
|
||||
interface Builder {
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
package com.yandex.div.core.dagger
|
||||
|
||||
import com.yandex.div.core.experiments.Experiment.MULTIPLE_STATE_CHANGE_ENABLED
|
||||
import com.yandex.div.core.util.ReportingSafeDrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view2.state.DivJoinedStateSwitcher
|
||||
import com.yandex.div.core.view2.state.DivMultipleStateSwitcher
|
||||
import com.yandex.div.core.view2.state.DivStateSwitcher
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
import com.yandex.yatagan.Binds
|
||||
import com.yandex.yatagan.Module
|
||||
import com.yandex.yatagan.Provides
|
||||
import javax.inject.Provider
|
||||
|
||||
@Module
|
||||
internal object Div2ViewModule {
|
||||
internal interface Div2ViewModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@DivViewScope
|
||||
fun provideStateSwitcher(
|
||||
@ExperimentFlag(MULTIPLE_STATE_CHANGE_ENABLED) multipleStateChangeEnabled: Boolean,
|
||||
joinedStateSwitcher: Provider<DivJoinedStateSwitcher>,
|
||||
multipleStateSwitcher: Provider<DivMultipleStateSwitcher>
|
||||
): DivStateSwitcher {
|
||||
return if (multipleStateChangeEnabled) multipleStateSwitcher.get() else joinedStateSwitcher.get()
|
||||
@Binds
|
||||
fun bindsDrawingPassOverrideStrategy(i: ReportingSafeDrawingPassOverrideStrategy): DrawingPassOverrideStrategy
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@DivViewScope
|
||||
fun provideStateSwitcher(
|
||||
@ExperimentFlag(MULTIPLE_STATE_CHANGE_ENABLED) multipleStateChangeEnabled: Boolean,
|
||||
joinedStateSwitcher: Provider<DivJoinedStateSwitcher>,
|
||||
multipleStateSwitcher: Provider<DivMultipleStateSwitcher>
|
||||
): DivStateSwitcher {
|
||||
return if (multipleStateChangeEnabled) multipleStateSwitcher.get() else joinedStateSwitcher.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.yandex.div.core.util
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
import com.yandex.div.core.Div2Logger
|
||||
import com.yandex.div.core.dagger.DivViewScope
|
||||
import com.yandex.div.core.view.SafeDrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import javax.inject.Inject
|
||||
|
||||
@DivViewScope
|
||||
internal class ReportingSafeDrawingPassOverrideStrategy @Inject constructor(
|
||||
private val divView: Div2View,
|
||||
private val logger: Div2Logger,
|
||||
) : SafeDrawingPassOverrideStrategy() {
|
||||
|
||||
override fun onFrameCancelled(listener: OnPreDrawListener, frameCancelCount: Int) {
|
||||
logger.logFrameCancelled(divView, "Frame cancelled by $listener")
|
||||
}
|
||||
|
||||
override fun onFrameCancelLimitExceeded(listener: OnPreDrawListener, frameCancelCount: Int) {
|
||||
logger.logFrameCancelLimitExceeded(
|
||||
divView,
|
||||
"Frame cancellation limit exceeded by $listener. Forcing frame drawing."
|
||||
)
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
package com.yandex.div.core.util
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
import com.yandex.div.core.Div2Logger
|
||||
import com.yandex.div.core.dagger.DivViewScope
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.widget.DrawingPassOverrideStrategy
|
||||
import javax.inject.Inject
|
||||
|
||||
@DivViewScope
|
||||
internal class SafeDrawingPassOverrideStrategy @Inject constructor(
|
||||
private val divView: Div2View,
|
||||
private val logger: Div2Logger,
|
||||
) : DrawingPassOverrideStrategy {
|
||||
|
||||
var frameCancelLimit: Int = DEFAULT_FRAME_CANCEL_LIMIT
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
frameCancelCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
private var frameCancelCount = 0
|
||||
|
||||
override fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean): Boolean {
|
||||
if (proceed) {
|
||||
frameCancelCount = 0
|
||||
return true
|
||||
} else if (frameCancelCount < frameCancelLimit) {
|
||||
frameCancelCount++
|
||||
logger.logFrameCancelled(divView, "Frame cancelled by $listener")
|
||||
return false
|
||||
} else if (frameCancelCount == frameCancelLimit) {
|
||||
frameCancelCount++
|
||||
logger.logFrameCancelLimitExceeded(
|
||||
divView,
|
||||
"Frame cancellation limit exceeded by $listener. Forcing frame drawing."
|
||||
)
|
||||
return true
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_FRAME_CANCEL_LIMIT = 3
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.transition.Transition
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.Visibility
|
||||
@@ -20,6 +19,7 @@ import com.yandex.div.core.util.isConstant
|
||||
import com.yandex.div.core.util.observeEdgeInsets
|
||||
import com.yandex.div.core.util.observeSize
|
||||
import com.yandex.div.core.util.observeTransform
|
||||
import com.yandex.div.core.view.onPreDrawListener
|
||||
import com.yandex.div.core.view2.BindingContext
|
||||
import com.yandex.div.core.view2.Div2View
|
||||
import com.yandex.div.core.view2.DivAccessibilityBinder
|
||||
@@ -301,7 +301,7 @@ internal class DivBaseBinder @Inject constructor(
|
||||
setTag(R.id.div_layout_provider_listener_id, listener)
|
||||
if (divView.clearVariablesListener != null) return
|
||||
|
||||
val clearVariablesListener = ViewTreeObserver.OnPreDrawListener {
|
||||
val clearVariablesListener = onPreDrawListener {
|
||||
variablesHolder.clear()
|
||||
divView.layoutSizes.forEach { (resolver, variableMap) ->
|
||||
variableMap.forEach {
|
||||
|
||||
@@ -5,6 +5,8 @@ import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view.onPreDrawListener
|
||||
import com.yandex.div.core.widget.AdaptiveMaxLines.Params
|
||||
|
||||
/**
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package com.yandex.div.core.widget
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
internal fun interface DrawingPassOverrideStrategy {
|
||||
|
||||
fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean): Boolean
|
||||
|
||||
object Default : DrawingPassOverrideStrategy {
|
||||
override fun overrideDrawingPass(listener: OnPreDrawListener, proceed: Boolean) = proceed
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.yandex.div.core.widget
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
internal fun onPreDrawListener(
|
||||
overrideStrategy: DrawingPassOverrideStrategy,
|
||||
action: () -> Boolean
|
||||
): ViewTreeObserver.OnPreDrawListener {
|
||||
return OverridableOnPreDrawListener(
|
||||
delegate = { action() },
|
||||
overrideStrategy
|
||||
)
|
||||
}
|
||||
|
||||
internal fun onPreDrawListener(
|
||||
overrideStrategy: DrawingPassOverrideStrategy,
|
||||
delegate: ViewTreeObserver.OnPreDrawListener
|
||||
): ViewTreeObserver.OnPreDrawListener {
|
||||
return OverridableOnPreDrawListener(
|
||||
delegate = delegate,
|
||||
overrideStrategy
|
||||
)
|
||||
}
|
||||
|
||||
internal class OverridableOnPreDrawListener(
|
||||
private val delegate: ViewTreeObserver.OnPreDrawListener,
|
||||
private val overrideStrategy: DrawingPassOverrideStrategy
|
||||
): ViewTreeObserver.OnPreDrawListener {
|
||||
|
||||
override fun onPreDraw(): Boolean {
|
||||
val proceed = delegate.onPreDraw()
|
||||
return overrideStrategy.overrideDrawingPass(delegate, proceed)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
package com.yandex.div.internal.widget
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
import com.yandex.div.core.widget.DrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.widget.onPreDrawListener
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view.onPreDrawListener
|
||||
import com.yandex.div.internal.KLog
|
||||
|
||||
/**
|
||||
@@ -15,7 +15,7 @@ internal class AutoEllipsizeHelper(private val textView: EllipsizedTextView) {
|
||||
*/
|
||||
var isEnabled = false
|
||||
|
||||
var drawingPassOverrideStrategy: DrawingPassOverrideStrategy = DrawingPassOverrideStrategy.Default
|
||||
var drawingPassOverrideStrategy: DrawingPassOverrideStrategy = DrawingPassOverrideStrategy.Safe
|
||||
|
||||
private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import android.util.AttributeSet
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.yandex.div.R
|
||||
import com.yandex.div.core.widget.DrawingPassOverrideStrategy
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
|
||||
open class EllipsizedTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ import org.mockito.kotlin.verify
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class SafeDrawingPassOverrideStrategyTest {
|
||||
class ReportingSafeDrawingPassOverrideStrategyTest {
|
||||
|
||||
private val divView = mock<Div2View>()
|
||||
private val logger = mock<Div2Logger>()
|
||||
|
||||
private val overrideStrategy = SafeDrawingPassOverrideStrategy(divView, logger)
|
||||
private val overrideStrategy = ReportingSafeDrawingPassOverrideStrategy(divView, logger)
|
||||
|
||||
@Test
|
||||
fun `no frame cancellation`() {
|
||||
@@ -3,6 +3,7 @@ package com.yandex.div.core.widget
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.TextView
|
||||
import com.yandex.div.core.view.DrawingPassOverrideStrategy
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -23,7 +24,7 @@ class AdaptiveMaxLinesTest {
|
||||
on { viewTreeObserver } doReturn viewTreeObserver
|
||||
}
|
||||
|
||||
private val underTest = AdaptiveMaxLines(textView, DrawingPassOverrideStrategy.Default)
|
||||
private val underTest = AdaptiveMaxLines(textView, DrawingPassOverrideStrategy.NoOp)
|
||||
|
||||
@Test
|
||||
fun `add pre draw listener when view already attached to window`() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[versions]
|
||||
agp = "8.8.2"
|
||||
android-lint = "31.8.2" # agp + "23.0.0"
|
||||
androidsvg = "1.4"
|
||||
buildTimeTracker = "5.0.1"
|
||||
coil = "3.0.4"
|
||||
@@ -66,6 +67,10 @@ androidx-work = "2.9.1"
|
||||
agp-gradle = { module = "com.android.tools.build:gradle", version.ref = "agp" }
|
||||
agp-gradleApi = { module = "com.android.tools.build:gradle-api", version.ref = "agp" }
|
||||
|
||||
android-lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "android-lint" }
|
||||
android-lint = { module = "com.android.tools.lint:lint", version.ref = "android-lint" }
|
||||
android-lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "android-lint" }
|
||||
|
||||
androidsvg-aar = { module = "com.caverock:androidsvg-aar", version.ref = "androidsvg" }
|
||||
|
||||
androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'org.jetbrains.kotlin.jvm'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.android.lint.api)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.android.lint)
|
||||
testImplementation(libs.android.lint.tests)
|
||||
}
|
||||
|
||||
tasks.withType(KotlinCompile).configureEach {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_17
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yandex.div.lint
|
||||
|
||||
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
|
||||
|
||||
class DivKitIssueRegistry : IssueRegistry() {
|
||||
|
||||
override val api = CURRENT_API
|
||||
|
||||
override val minApi = 8
|
||||
|
||||
override val issues = listOf(OnPreDrawListenerIssue.get())
|
||||
|
||||
override val vendor = Vendor(
|
||||
feedbackUrl = "https://divkit.tech/",
|
||||
identifier = "com.yandex.div",
|
||||
vendorName = "DivKit Open Source Project"
|
||||
)
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.yandex.div.lint
|
||||
|
||||
import com.android.tools.lint.client.api.UElementHandler
|
||||
import com.android.tools.lint.detector.api.Detector
|
||||
import com.android.tools.lint.detector.api.JavaContext
|
||||
import com.android.tools.lint.detector.api.Location
|
||||
import com.android.tools.lint.detector.api.SourceCodeScanner
|
||||
import org.jetbrains.uast.UCallExpression
|
||||
import org.jetbrains.uast.UCallableReferenceExpression
|
||||
import org.jetbrains.uast.UClass
|
||||
import org.jetbrains.uast.UElement
|
||||
import org.jetbrains.uast.ULambdaExpression
|
||||
import org.jetbrains.uast.UParameter
|
||||
import org.jetbrains.uast.getParameterForArgument
|
||||
import org.jetbrains.uast.getParentOfType
|
||||
import org.jetbrains.uast.getQualifiedName
|
||||
import org.jetbrains.uast.toUElementOfType
|
||||
import org.jetbrains.uast.util.isConstructorCall
|
||||
|
||||
class OnPreDrawListenerDetector : Detector(), SourceCodeScanner {
|
||||
|
||||
override fun getApplicableUastTypes(): List<Class<out UElement>> = listOf(
|
||||
UClass::class.java,
|
||||
ULambdaExpression::class.java,
|
||||
UCallableReferenceExpression::class.java
|
||||
)
|
||||
|
||||
override fun createUastHandler(context: JavaContext): UElementHandler = OnPreDrawListenerVisitor(context)
|
||||
}
|
||||
|
||||
class OnPreDrawListenerVisitor(
|
||||
private val context: JavaContext
|
||||
) : UElementHandler() {
|
||||
|
||||
override fun visitClass(node: UClass) {
|
||||
val usage = node.uastSuperTypes.find { typeReference ->
|
||||
typeReference.getQualifiedName() == TARGET_INTERFACE_FQN
|
||||
}
|
||||
if (usage != null) {
|
||||
reportIssue(scopeClass = node, usage = usage)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitLambdaExpression(node: ULambdaExpression) {
|
||||
val callExpression = node.uastParent as? UCallExpression ?: return
|
||||
val constructorExpression = if (callExpression.isConstructorCall()) {
|
||||
callExpression.classReference
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val lambdaType = node.functionalInterfaceType?.canonicalText
|
||||
|
||||
if (lambdaType == TARGET_INTERFACE_FQN) {
|
||||
val nodeClass = node.getParentOfType<UClass>()
|
||||
|
||||
val usageNode = if (constructorExpression.getQualifiedName() == TARGET_INTERFACE_FQN) {
|
||||
constructorExpression!!
|
||||
} else {
|
||||
node
|
||||
}
|
||||
|
||||
val location = if (constructorExpression.getQualifiedName() == TARGET_INTERFACE_FQN) {
|
||||
context.getCallLocation(
|
||||
callExpression,
|
||||
includeReceiver = true,
|
||||
includeArguments = false
|
||||
)
|
||||
} else {
|
||||
context.getLocation(node)
|
||||
}
|
||||
|
||||
reportIssue(
|
||||
scopeClass = nodeClass,
|
||||
usage = usageNode,
|
||||
location = location
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCallableReferenceExpression(node: UCallableReferenceExpression) {
|
||||
val callExpression = node.uastParent as? UCallExpression ?: return
|
||||
val referenceParameter = callExpression.getParameterForArgument(node).toUElementOfType<UParameter>() ?: return
|
||||
val referenceType = referenceParameter.typeReference?.getQualifiedName()
|
||||
|
||||
if (referenceType == TARGET_INTERFACE_FQN) {
|
||||
val nodeClass = node.getParentOfType<UClass>()
|
||||
reportIssue(
|
||||
scopeClass = nodeClass,
|
||||
usage = node
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportIssue(
|
||||
scopeClass: UClass?,
|
||||
usage: UElement,
|
||||
location: Location = context.getLocation(usage)
|
||||
) {
|
||||
context.report(
|
||||
issue = OnPreDrawListenerIssue.get(),
|
||||
scopeClass = scopeClass,
|
||||
location = location,
|
||||
message = OnPreDrawListenerIssue.fullDescription()
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TARGET_INTERFACE_FQN = "android.view.ViewTreeObserver.OnPreDrawListener"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.yandex.div.lint
|
||||
|
||||
import com.android.tools.lint.detector.api.Category
|
||||
import com.android.tools.lint.detector.api.Implementation
|
||||
import com.android.tools.lint.detector.api.Issue
|
||||
import com.android.tools.lint.detector.api.Scope
|
||||
import com.android.tools.lint.detector.api.Severity
|
||||
|
||||
object OnPreDrawListenerIssue {
|
||||
|
||||
const val ID = "OnPreDrawListenerIssue"
|
||||
|
||||
private const val DESCRIPTION = "ViewTreeObserver.OnPreDrawListener is potentially unsafe"
|
||||
private const val EXPLANATION = """Return of `false` from `ViewTreeObserver.OnPreDrawListener.onPreDraw()` may break the drawing of the entire screen.
|
||||
Replace it with `com.yandex.div.core.view.OverridableOnPreDrawListener` or `com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...)` function."""
|
||||
|
||||
private val issue = Issue.create(
|
||||
id = ID,
|
||||
briefDescription = DESCRIPTION,
|
||||
explanation = EXPLANATION,
|
||||
category = Category.CORRECTNESS,
|
||||
priority = 8,
|
||||
severity = Severity.ERROR,
|
||||
implementation = Implementation(
|
||||
OnPreDrawListenerDetector::class.java,
|
||||
Scope.JAVA_FILE_SCOPE
|
||||
)
|
||||
)
|
||||
|
||||
fun get(): Issue = issue
|
||||
|
||||
fun fullDescription(): String = "$DESCRIPTION\n$EXPLANATION"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
com.yandex.div.lint.DivKitIssueRegistry
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
package com.yandex.div.lint
|
||||
|
||||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
|
||||
import com.android.tools.lint.checks.infrastructure.TestFile
|
||||
import com.android.tools.lint.checks.infrastructure.TestMode
|
||||
import com.android.tools.lint.detector.api.Detector
|
||||
import com.android.tools.lint.detector.api.Issue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
class OnPreDrawListenerDetectorTest : LintDetectorTest() {
|
||||
|
||||
private val declarationFile: TestFile = kotlin(
|
||||
"""
|
||||
package android.view
|
||||
|
||||
class ViewTreeObserver {
|
||||
fun interface OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean
|
||||
}
|
||||
}
|
||||
"""
|
||||
).indented()
|
||||
|
||||
override fun getDetector(): Detector = OnPreDrawListenerDetector()
|
||||
|
||||
override fun getIssues(): List<Issue> = listOf(OnPreDrawListenerIssue.get())
|
||||
|
||||
@Test
|
||||
fun `issue detected in implementation`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
class OnPreDrawListenerImpl : ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean = true
|
||||
}
|
||||
|
||||
val onPreDrawListener = OnPreDrawListenerImpl()
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/OnPreDrawListenerImpl.kt:5: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
class OnPreDrawListenerImpl : ViewTreeObserver.OnPreDrawListener {
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue detected in anonymous implementation`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
val onPreDrawListener = object : ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean = true
|
||||
}
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/test.kt:5: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
val onPreDrawListener = object : ViewTreeObserver.OnPreDrawListener {
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue does not detected in implicit implementation`() {
|
||||
val testFile = kotlin("""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
@Suppress("OnPreDrawListenerIssue")
|
||||
class OnPreDrawListenerImpl : ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean = true
|
||||
}
|
||||
|
||||
fun onPreDrawListener(): ViewTreeObserver.OnPreDrawListener {
|
||||
return OnPreDrawListenerImpl()
|
||||
}
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.skipTestModes(TestMode.SUPPRESSIBLE)
|
||||
.run()
|
||||
.expectClean()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue detected in SAM constructor`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
val onPreDrawListener = OnPreDrawListener { true }
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.skipTestModes(TestMode.SUPPRESSIBLE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/test.kt:5: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
val onPreDrawListener = OnPreDrawListener { true }
|
||||
~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue detected in qualified SAM constructor`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
val onPreDrawListener = ViewTreeObserver.OnPreDrawListener { true }
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.skipTestModes(TestMode.SUPPRESSIBLE)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/test.kt:5: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
val onPreDrawListener = ViewTreeObserver.OnPreDrawListener { true }
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue detected in lambda SAM conversion`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
class Wrapper(
|
||||
private val count: Int,
|
||||
private val listener: ViewTreeObserver.OnPreDrawListener
|
||||
)
|
||||
|
||||
fun wrap(listener: ViewTreeObserver.OnPreDrawListener): Wrapper {
|
||||
return Wrapper(0, listener)
|
||||
}
|
||||
|
||||
fun doWrapConstructorCall() {
|
||||
Wrapper(0, listener = { true })
|
||||
}
|
||||
|
||||
fun doWrapMethodCall() {
|
||||
wrap(listener = { true })
|
||||
}
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.skipTestModes(TestMode.SUPPRESSIBLE, TestMode.SOURCE_TRANSFORMATION_GROUP)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/Wrapper.kt:15: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
Wrapper(0, listener = { true })
|
||||
~~~~~~~~
|
||||
src/test/Wrapper.kt:19: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
wrap(listener = { true })
|
||||
~~~~~~~~
|
||||
2 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `issue detected in method reference SAM conversion`() {
|
||||
val testFile = kotlin(
|
||||
"""
|
||||
package test
|
||||
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
class Wrapper(private val listener: ViewTreeObserver.OnPreDrawListener)
|
||||
|
||||
fun wrap(listener: ViewTreeObserver.OnPreDrawListener): Wrapper {
|
||||
return Wrapper(listener)
|
||||
}
|
||||
|
||||
fun onPreDraw(): Boolean = true
|
||||
|
||||
fun doWrapConstructorCall() {
|
||||
Wrapper(listener = ::onPreDraw)
|
||||
}
|
||||
|
||||
fun doWrapMethodCall() {
|
||||
wrap(listener = ::onPreDraw)
|
||||
}
|
||||
"""
|
||||
).indented()
|
||||
|
||||
lint()
|
||||
.files(declarationFile, testFile)
|
||||
.allowMissingSdk()
|
||||
.skipTestModes(TestMode.SUPPRESSIBLE, TestMode.SOURCE_TRANSFORMATION_GROUP)
|
||||
.run()
|
||||
.expect(
|
||||
"""
|
||||
src/test/Wrapper.kt:14: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
Wrapper(listener = ::onPreDraw)
|
||||
~~~~~~~~~~~
|
||||
src/test/Wrapper.kt:18: Error: ViewTreeObserver.OnPreDrawListener is potentially unsafe
|
||||
Return of false from ViewTreeObserver.OnPreDrawListener.onPreDraw() may break the drawing of the entire screen.
|
||||
Replace it with com.yandex.div.core.view.OverridableOnPreDrawListener or com.yandex.div.core.view.OnPreDrawListeners.onPreDrawListener(...) function. [OnPreDrawListenerIssue]
|
||||
wrap(listener = ::onPreDraw)
|
||||
~~~~~~~~~~~
|
||||
2 errors, 0 warnings
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ include ':divkit-regression-testing'
|
||||
include ':expression-test-common'
|
||||
include ':fonts'
|
||||
include ':glide'
|
||||
include ':lint-rules'
|
||||
include ':logging'
|
||||
include ':picasso'
|
||||
include ':sample'
|
||||
|
||||
Reference in New Issue
Block a user