fix(android): enforce minSdk compatibility

This commit is contained in:
edde746
2026-07-13 17:00:50 +02:00
parent ae71b7bc64
commit 822657972d
10 changed files with 31 additions and 22 deletions
+4
View File
@@ -176,6 +176,10 @@ jobs:
working-directory: android working-directory: android
run: ./gradlew :app:testDebugUnitTest :saf_util:testDebugUnitTest :libass:testDebugUnitTest -x :app:compileFlutterBuildDebug --continue run: ./gradlew :app:testDebugUnitTest :saf_util:testDebugUnitTest :libass:testDebugUnitTest -x :app:compileFlutterBuildDebug --continue
- name: Check Android API compatibility
working-directory: android
run: ./gradlew :app:lintDebug
native-format: native-format:
name: Native Formatting name: Native Formatting
runs-on: ubuntu-latest runs-on: ubuntu-latest
+6
View File
@@ -166,6 +166,12 @@ android {
jniLibs.srcDir(File(mpvDir, "libcxx/jni")) jniLibs.srcDir(File(mpvDir, "libcxx/jni"))
} }
} }
lint {
// Enforce the app-owned minSdk boundary without auditing upstream AndroidX.
checkDependencies = false
checkOnly += setOf("NewApi")
}
} }
flutter { flutter {
@@ -21,6 +21,7 @@ import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import com.edde746.plezy.exoplayer.ExoPlayerPlugin import com.edde746.plezy.exoplayer.ExoPlayerPlugin
import com.edde746.plezy.mpv.MpvAudioPlayerPlugin import com.edde746.plezy.mpv.MpvAudioPlayerPlugin
import com.edde746.plezy.mpv.MpvPlayerPlugin import com.edde746.plezy.mpv.MpvPlayerPlugin
@@ -676,6 +677,7 @@ class MainActivity : FlutterActivity() {
} }
} }
@RequiresApi(Build.VERSION_CODES.O)
private fun isPipPermissionGranted(): Boolean { private fun isPipPermissionGranted(): Boolean {
val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return appOpsManager.checkOpNoThrow( return appOpsManager.checkOpNoThrow(
@@ -685,6 +687,7 @@ class MainActivity : FlutterActivity() {
) == AppOpsManager.MODE_ALLOWED ) == AppOpsManager.MODE_ALLOWED
} }
@RequiresApi(Build.VERSION_CODES.O)
private fun buildPipParams(width: Int, height: Int, autoEnterEnabled: Boolean? = null): PictureInPictureParams { private fun buildPipParams(width: Int, height: Int, autoEnterEnabled: Boolean? = null): PictureInPictureParams {
val (w, h) = if (width <= 0 || height <= 0) { val (w, h) = if (width <= 0 || height <= 0) {
Pair(16, 9) Pair(16, 9)
@@ -3,6 +3,7 @@ package com.edde746.plezy.exoplayer
import android.os.Build import android.os.Build
import android.view.SurfaceControl import android.view.SurfaceControl
import android.view.SurfaceView import android.view.SurfaceView
import androidx.annotation.RequiresApi
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import kotlin.math.abs import kotlin.math.abs
@@ -101,6 +102,7 @@ internal class AssLatencyCalibrator(
/** Never called while holding [lock]: applyTransactionToFrame is a binder call and runs on the /** Never called while holding [lock]: applyTransactionToFrame is a binder call and runs on the
* GL swap path. The native slot allocation is atomic, so concurrent callers are safe. */ * GL swap path. The native slot allocation is atomic, so concurrent callers are safe. */
@RequiresApi(34)
private fun attach(surface: SurfaceView, releaseTimeNs: Long, source: Int) { private fun attach(surface: SurfaceView, releaseTimeNs: Long, source: Int) {
try { try {
val tx = SurfaceControl.Transaction() val tx = SurfaceControl.Transaction()
@@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.annotation.RequiresApi
import androidx.media3.common.C import androidx.media3.common.C
import androidx.media3.common.Format import androidx.media3.common.Format
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
@@ -2057,6 +2058,7 @@ class ExoPlayerCore(private val activity: Activity) : Player.Listener {
.build() .build()
} }
@RequiresApi(Build.VERSION_CODES.Q)
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun isDirectPlaybackSupportedApi29( private fun isDirectPlaybackSupportedApi29(
audioFormat: AudioFormat, audioFormat: AudioFormat,
@@ -5,6 +5,7 @@ import android.content.Context
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.ImageReader import android.media.ImageReader
import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
@@ -135,9 +136,15 @@ class MpvPlayerCore(
Log.d(TAG, "Created MPV placeholder surface") Log.d(TAG, "Created MPV placeholder surface")
} }
@Suppress("DEPRECATION")
private fun currentDisplayFpsOverride(): String? { private fun currentDisplayFpsOverride(): String? {
if (audioOnly) return null if (audioOnly) return null
val refreshRate = activity.display?.mode?.refreshRate ?: return null val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.display
} else {
activity.windowManager.defaultDisplay
}
val refreshRate = display?.mode?.refreshRate ?: return null
if (refreshRate <= 0f) return null if (refreshRate <= 0f) return null
return refreshRate.toString() return refreshRate.toString()
} }
@@ -24,15 +24,14 @@ internal object MediaCodecQuery {
} }
fun isHardwareAccelerated(info: MediaCodecInfo): Boolean { fun isHardwareAccelerated(info: MediaCodecInfo): Boolean {
val sdkInt = Build.VERSION.SDK_INT
val name = info.name val name = info.name
return if (sdkInt >= Build.VERSION_CODES.Q) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
isHardwareAccelerated(sdkInt, info.isHardwareAccelerated, name) isHardwareAccelerated(Build.VERSION.SDK_INT, info.isHardwareAccelerated, name)
} else { } else {
// MediaCodecInfo.isHardwareAccelerated() does not exist before API 29. // MediaCodecInfo.isHardwareAccelerated() does not exist before API 29.
// Keep the call inside the version gate so older Fire OS releases do not // Keep the call inside the version gate so older Fire OS releases do not
// fail with NoSuchMethodError while initializing playback. // fail with NoSuchMethodError while initializing playback.
isHardwareAccelerated(sdkInt, false, name) isHardwareAccelerated(Build.VERSION.SDK_INT, false, name)
} }
} }
@@ -3,8 +3,6 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<!-- Disable Android's default green focus highlight that appears during d-pad/keyboard navigation -->
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
@@ -15,30 +13,24 @@
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<!-- Disable Android's default green focus highlight that appears during d-pad/keyboard navigation -->
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<!-- Splash theme variants for setSplashScreenTheme --> <!-- Splash theme variants for setSplashScreenTheme -->
<style name="SplashTheme.Dark" parent="@android:style/Theme.Black.NoTitleBar"> <style name="SplashTheme.Dark" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_dark</item> <item name="android:windowBackground">@color/splash_dark</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.Oled" parent="@android:style/Theme.Black.NoTitleBar"> <style name="SplashTheme.Oled" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_oled</item> <item name="android:windowBackground">@color/splash_oled</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.Light" parent="@android:style/Theme.Black.NoTitleBar"> <style name="SplashTheme.Light" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_light</item> <item name="android:windowBackground">@color/splash_light</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.System" parent="@android:style/Theme.Black.NoTitleBar"> <style name="SplashTheme.System" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
</resources> </resources>
@@ -3,8 +3,6 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<!-- Disable Android's default green focus highlight that appears during d-pad/keyboard navigation -->
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
@@ -15,30 +13,24 @@
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<!-- Disable Android's default green focus highlight that appears during d-pad/keyboard navigation -->
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<!-- Splash theme variants for setSplashScreenTheme --> <!-- Splash theme variants for setSplashScreenTheme -->
<style name="SplashTheme.Dark" parent="@android:style/Theme.Light.NoTitleBar"> <style name="SplashTheme.Dark" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_dark</item> <item name="android:windowBackground">@color/splash_dark</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.Oled" parent="@android:style/Theme.Light.NoTitleBar"> <style name="SplashTheme.Oled" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_oled</item> <item name="android:windowBackground">@color/splash_oled</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.Light" parent="@android:style/Theme.Light.NoTitleBar"> <style name="SplashTheme.Light" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_light</item> <item name="android:windowBackground">@color/splash_light</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
<style name="SplashTheme.System" parent="@android:style/Theme.Light.NoTitleBar"> <style name="SplashTheme.System" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item> <item name="android:windowBackground">@color/splash_background</item>
<item name="android:defaultFocusHighlightEnabled">false</item>
<item name="android:colorControlHighlight">@android:color/transparent</item> <item name="android:colorControlHighlight">@android:color/transparent</item>
</style> </style>
</resources> </resources>
+3 -1
View File
@@ -1,6 +1,8 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true # All bundled dependencies use AndroidX. Jetifier also cannot process modern
# JVM-only test artifacts when Android lint builds dependency models.
android.enableJetifier=false
# This builtInKotlin flag was added automatically by Flutter migrator # This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator # This newDsl flag was added automatically by Flutter migrator