feat(player): add mobile edge adjustments

This commit is contained in:
edde746
2026-06-14 13:40:16 +02:00
parent e7809b3160
commit a861a4110f
13 changed files with 1184 additions and 5 deletions
+1
View File
@@ -5,6 +5,7 @@
<!-- Internet access permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<!-- PIP and media session foreground service permissions -->
@@ -10,6 +10,7 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.SurfaceTexture
import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -24,6 +25,7 @@ import android.view.KeyEvent
import android.view.TextureView
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import androidx.core.content.FileProvider
@@ -40,6 +42,7 @@ import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterShellArgs
import io.flutter.plugin.common.MethodChannel
import java.io.File
import kotlin.math.roundToInt
class MainActivity : FlutterActivity() {
@@ -78,12 +81,14 @@ class MainActivity : FlutterActivity() {
private val EXTERNAL_PLAYER_CHANNEL = "com.plezy/external_player"
private val THEME_CHANNEL = "com.plezy/theme"
private val DEVICE_CHANNEL = "com.plezy/device"
private val DEVICE_ADJUSTMENT_CHANNEL = "com.plezy/device_adjustment"
private val TEXT_INPUT_CHANNEL = "com.plezy/text_input"
private val APP_EXIT_CHANNEL = "com.plezy/app_exit"
private val APP_FOREGROUND_CHANNEL = "com.plezy/app_foreground"
private var watchNextPlugin: WatchNextPlugin? = null
private var nativeTextInputFocused = false
private var pendingExternalPlayerResult: MethodChannel.Result? = null
private var originalWindowBrightness: Float? = null
private inline fun logTextInputDiag(message: () -> String) {
if (TEXT_INPUT_DIAGNOSTICS_ENABLED) {
@@ -460,6 +465,10 @@ class MainActivity : FlutterActivity() {
}
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, DEVICE_ADJUSTMENT_CHANNEL).setMethodCallHandler { call, result ->
handleDeviceAdjustmentCall(call.method, call.arguments, result)
}
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, TEXT_INPUT_CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"setNativeTextInputFocused" -> {
@@ -702,6 +711,94 @@ class MainActivity : FlutterActivity() {
}
}
private fun handleDeviceAdjustmentCall(method: String, arguments: Any?, result: MethodChannel.Result) {
try {
when (method) {
"getBrightness" -> result.success(getScreenBrightnessFraction())
"setBrightness" -> {
setScreenBrightnessFraction(argumentAsDouble(arguments))
result.success(null)
}
"restoreBrightness" -> {
restoreScreenBrightness()
result.success(null)
}
"getMediaVolume" -> result.success(getMediaVolumeFraction())
"setMediaVolume" -> {
setMediaVolumeFraction(argumentAsDouble(arguments))
result.success(null)
}
else -> result.notImplemented()
}
} catch (e: IllegalArgumentException) {
result.error("INVALID_ARGUMENT", e.message ?: e.javaClass.simpleName, null)
} catch (e: Exception) {
result.error("DEVICE_ADJUSTMENT_FAILED", e.message ?: e.javaClass.simpleName, null)
}
}
private fun argumentAsDouble(arguments: Any?): Double {
val value = (arguments as? Number)?.toDouble()
?: throw IllegalArgumentException("Expected a numeric value")
if (value.isNaN() || value.isInfinite()) {
throw IllegalArgumentException("Expected a finite numeric value")
}
return value.coerceIn(0.0, 1.0)
}
private fun getScreenBrightnessFraction(): Double {
val windowBrightness = window.attributes.screenBrightness
if (windowBrightness >= 0f) return windowBrightness.coerceIn(0f, 1f).toDouble()
return try {
Settings.System.getInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS).coerceIn(0, 255) / 255.0
} catch (e: Settings.SettingNotFoundException) {
0.5
}
}
private fun setScreenBrightnessFraction(value: Double) {
if (originalWindowBrightness == null) originalWindowBrightness = window.attributes.screenBrightness
val attributes = window.attributes
attributes.screenBrightness = value.coerceIn(0.0, 1.0).toFloat()
window.attributes = attributes
}
private fun restoreScreenBrightness() {
val attributes = window.attributes
attributes.screenBrightness = originalWindowBrightness ?: WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
window.attributes = attributes
originalWindowBrightness = null
}
private fun getMediaVolumeFraction(): Double {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val minVolume = streamMinVolume(audioManager)
if (maxVolume <= minVolume) return 0.0
val volume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).coerceIn(minVolume, maxVolume)
return (volume - minVolume).toDouble() / (maxVolume - minVolume).toDouble()
}
private fun setMediaVolumeFraction(value: Double) {
val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
val minVolume = streamMinVolume(audioManager)
val target = (minVolume + value.coerceIn(0.0, 1.0) * (maxVolume - minVolume))
.roundToInt()
.coerceIn(minVolume, maxVolume)
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, target, 0)
}
private fun streamMinVolume(audioManager: AudioManager): Int {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC)
} else {
0
}
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
flutterEngine?.let { engine ->