mirror of
https://github.com/oxyroid/M3UAndroid.git
synced 2025-05-17 19:35:58 +08:00
lint: UseHelperInsteadOfWindowInsets.
This commit is contained in:
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@ -24,6 +24,7 @@
|
||||
<option value="$PROJECT_DIR$/features/main" />
|
||||
<option value="$PROJECT_DIR$/features/scheme" />
|
||||
<option value="$PROJECT_DIR$/features/setting" />
|
||||
<option value="$PROJECT_DIR$/lint" />
|
||||
<option value="$PROJECT_DIR$/ui" />
|
||||
</set>
|
||||
</option>
|
||||
|
@ -118,8 +118,7 @@ class MainActivity : ComponentActivity() {
|
||||
applyConfiguration()
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// 1. window inset controller cannot take effect in orientation changing quickly.
|
||||
@Helper.WindowInsetsAllowed
|
||||
private fun applyConfiguration() {
|
||||
val navigationBarsVisibility = helper.navigationBarsVisibility
|
||||
val statusBarsVisibility = helper.statusBarsVisibility
|
||||
@ -138,6 +137,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@Helper.WindowInsetsAllowed
|
||||
private fun WindowInsetsControllerCompat.default(@InsetsType types: Int) {
|
||||
when (types) {
|
||||
WindowInsetsCompat.Type.navigationBars() -> {
|
||||
|
@ -32,6 +32,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
lintPublish(project(":lint"))
|
||||
|
||||
implementation(libs.androidx.core.core.ktx)
|
||||
implementation(libs.androidx.appcompat.appcompat)
|
||||
implementation(libs.androidx.compose.ui.ui)
|
||||
|
@ -4,7 +4,7 @@ enum class UBoolean {
|
||||
True, False, Unspecified
|
||||
}
|
||||
|
||||
val Boolean.ub: UBoolean get() = if (this) UBoolean.True else UBoolean.False
|
||||
val Boolean.u: UBoolean get() = if (this) UBoolean.True else UBoolean.False
|
||||
|
||||
val UBoolean.actual: Boolean?
|
||||
get() = when (this) {
|
||||
|
@ -24,7 +24,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.media3.common.PlaybackException
|
||||
import androidx.media3.common.Player
|
||||
import com.m3u.core.annotation.ClipMode
|
||||
import com.m3u.core.unspecified.ub
|
||||
import com.m3u.core.unspecified.u
|
||||
import com.m3u.core.util.basic.isNotEmpty
|
||||
import com.m3u.core.util.context.toast
|
||||
import com.m3u.features.live.components.DlnaDevicesBottomSheet
|
||||
@ -55,8 +55,8 @@ internal fun LiveRoute(
|
||||
val searching by viewModel.searching.collectAsStateWithLifecycle()
|
||||
|
||||
val maskState = rememberMaskState { visible ->
|
||||
helper.statusBarsVisibility = visible.ub
|
||||
helper.navigationBarsVisibility = false.ub
|
||||
helper.statusBarsVisibility = visible.u
|
||||
helper.navigationBarsVisibility = false.u
|
||||
}
|
||||
var isPipMode by remember { mutableStateOf(false) }
|
||||
|
||||
@ -80,8 +80,8 @@ internal fun LiveRoute(
|
||||
}
|
||||
}
|
||||
darkMode = true
|
||||
statusBarsVisibility = false.ub
|
||||
navigationBarsVisibility = false.ub
|
||||
statusBarsVisibility = false.u
|
||||
navigationBarsVisibility = false.u
|
||||
onPipModeChanged = OnPipModeChanged { info ->
|
||||
isPipMode = info.isInPictureInPictureMode
|
||||
if (!isPipMode) {
|
||||
|
@ -39,6 +39,8 @@ com-google-android-material = "1.9.0"
|
||||
androidx-test-uiautomator = "2.2.0"
|
||||
androidx-benchmark = "1.1.1"
|
||||
|
||||
lint = "31.1.2"
|
||||
|
||||
[libraries]
|
||||
androidx-core-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
|
||||
androidx-core-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidx-core-splashscreen" }
|
||||
@ -107,6 +109,8 @@ androidx-test-espresso-espresso-core = { group = "androidx.test.espresso", name
|
||||
androidx-test-uiautomator-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidx-test-uiautomator" }
|
||||
androidx-benchmark-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidx-benchmark" }
|
||||
|
||||
com-android-tools-lint-lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" }
|
||||
|
||||
[plugins]
|
||||
com-android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
||||
com-android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
|
||||
|
1
lint/.gitignore
vendored
Normal file
1
lint/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
9
lint/build.gradle.kts
Normal file
9
lint/build.gradle.kts
Normal file
@ -0,0 +1,9 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
id("kotlin")
|
||||
id("com.android.lint")
|
||||
}
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
compileOnly(libs.com.android.tools.lint.lint.api)
|
||||
}
|
0
lint/consumer-rules.pro
Normal file
0
lint/consumer-rules.pro
Normal file
21
lint/proguard-rules.pro
vendored
Normal file
21
lint/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,106 @@
|
||||
package com.m3u.lint.helper
|
||||
|
||||
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.LintFix
|
||||
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 com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.uast.UCallExpression
|
||||
import org.jetbrains.uast.getContainingUMethod
|
||||
|
||||
class UseHelperInsteadOfWindowInsetsDetector : Detector(), SourceCodeScanner {
|
||||
override fun getApplicableMethodNames(): List<String> = listOf("show", "hide")
|
||||
|
||||
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
|
||||
val evaluator = context.evaluator
|
||||
val marked =
|
||||
node.getContainingUMethod()
|
||||
?.uAnnotations
|
||||
?.find { it.qualifiedName == ANNOTATION_NAME } != null
|
||||
if (marked) return
|
||||
if (evaluator.isMemberInClass(method, "androidx.core.view.WindowInsetsControllerCompat")) {
|
||||
val isStatusBars = node.getArgumentForParameter(0)?.evaluate() == 1
|
||||
val visible = node.methodName == "show"
|
||||
reportUsage(
|
||||
context = context,
|
||||
node = node,
|
||||
isStatusBars = isStatusBars,
|
||||
visible = visible
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportUsage(
|
||||
context: JavaContext,
|
||||
node: UCallExpression,
|
||||
isStatusBars: Boolean,
|
||||
visible: Boolean
|
||||
) {
|
||||
val property = if (isStatusBars) "status" else "navigation"
|
||||
val value = if (visible) "true" else "false"
|
||||
val expression = "helper.${property}BarsVisibility = ${value}.u"
|
||||
val data = LintFix.LintFixGroup(
|
||||
displayName = "display",
|
||||
familyName = "family",
|
||||
type = LintFix.GroupType.ALTERNATIVES,
|
||||
fixes = listOf(
|
||||
LintFix.create()
|
||||
.name("Replace with $expression")
|
||||
.replace()
|
||||
.imports(
|
||||
"com.m3u.core.unspecified.u",
|
||||
"com.m3u.ui.model.Helper"
|
||||
)
|
||||
.with(expression)
|
||||
.autoFix(robot = true, independent = true)
|
||||
.build(),
|
||||
LintFix.create()
|
||||
.name("Marked with @$CHILD_ANNOTATION_NAME annotation")
|
||||
.annotate("@$ANNOTATION_NAME")
|
||||
.autoFix(robot = true, independent = false)
|
||||
.build()
|
||||
)
|
||||
)
|
||||
|
||||
context.report(
|
||||
issue = UseHelperIssue,
|
||||
location = context.getLocation(node),
|
||||
message = EXPLANATION,
|
||||
quickfixData = data
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CHILD_ANNOTATION_NAME = "WindowInsetsAllowed"
|
||||
private const val ANNOTATION_NAME = "com.m3u.ui.model.Helper.${CHILD_ANNOTATION_NAME}"
|
||||
private const val BRIEF_DESCRIPTION = "Helper instead of WindowInsetsControllerCompat"
|
||||
val EXPLANATION = """
|
||||
Usage of WindowInsetsControllerCompat to control the system bars is not allowed in this project.
|
||||
```kotlin
|
||||
// Get helper instance in composable scope
|
||||
@Composable
|
||||
fun Screen() {
|
||||
val helper = LocalHelper.current
|
||||
}
|
||||
// then use helper to control system bars visibility
|
||||
helper.statusBarsVisibility = true.u
|
||||
```
|
||||
""".trimIndent()
|
||||
val UseHelperIssue = Issue.create(
|
||||
id = "UseHelperIssue",
|
||||
briefDescription = BRIEF_DESCRIPTION,
|
||||
explanation = EXPLANATION,
|
||||
category = Category.CORRECTNESS,
|
||||
severity = Severity.FATAL,
|
||||
implementation = Implementation(
|
||||
UseHelperInsteadOfWindowInsetsDetector::class.java,
|
||||
Scope.JAVA_FILE_SCOPE
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.m3u.lint.helper
|
||||
|
||||
import com.android.tools.lint.client.api.IssueRegistry
|
||||
import com.android.tools.lint.client.api.Vendor
|
||||
import com.android.tools.lint.detector.api.Issue
|
||||
import com.m3u.lint.helper.UseHelperInsteadOfWindowInsetsDetector.Companion.UseHelperIssue
|
||||
|
||||
class UseHelperInsteadOfWindowInsetsIssueRegistry : IssueRegistry() {
|
||||
override val issues: List<Issue>
|
||||
get() = listOf(UseHelperIssue)
|
||||
override val vendor: Vendor = Vendor(
|
||||
vendorName = "M3UAndroid Project",
|
||||
feedbackUrl = "https://t.me/m3u_android_chat",
|
||||
contact = "https://t.me/sortBy"
|
||||
)
|
||||
}
|
@ -0,0 +1 @@
|
||||
com.m3u.lint.helper.UseHelperInsteadOfWindowInsetsIssueRegistry
|
@ -32,3 +32,4 @@ include(
|
||||
":features:about"
|
||||
)
|
||||
include(":benchmark")
|
||||
include(":lint")
|
||||
|
@ -20,6 +20,9 @@ typealias OnPipModeChanged = Consumer<PictureInPictureModeChangedInfo>
|
||||
|
||||
@Stable
|
||||
interface Helper {
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class WindowInsetsAllowed
|
||||
|
||||
var title: String
|
||||
var actions: List<Action>
|
||||
var fob: Fob?
|
||||
|
Reference in New Issue
Block a user