fix(player): фон не глохнет — запрос уведомлений + исключение из оптимизации батареи
На OnePlus/ColorOS радио глохло в фоне даже с wake mode. Причины: POST_NOTIFICATIONS не выдан (медиа-уведомление не показывалось → foreground-сервис хрупкий) и приложение не в вайтлисте Doze. MainActivity на старте запрашивает POST_NOTIFICATIONS (13+), затем системный диалог REQUEST_IGNORE_BATTERY_OPTIMIZATIONS (один раз). v1.4 / versionCode 5 (clean-сборка).
This commit is contained in:
@@ -15,8 +15,8 @@ android {
|
|||||||
applicationId = "com.radiola"
|
applicationId = "com.radiola"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 4
|
versionCode = 5
|
||||||
versionName = "1.3"
|
versionName = "1.4"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
<!-- Держать CPU/Wi-Fi активными во время проигрывания при выключенном экране
|
<!-- Держать CPU/Wi-Fi активными во время проигрывания при выключенном экране
|
||||||
(иначе поток глохнет в фоне — особенно в машине по Bluetooth). -->
|
(иначе поток глохнет в фоне — особенно в машине по Bluetooth). -->
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<!-- Просить исключение из оптимизации батареи (Doze/ColorOS душат фоновое аудио). -->
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".RadiolaApplication"
|
android:name=".RadiolaApplication"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.radiola
|
package com.radiola
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
@@ -49,6 +50,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var settingsRepository: com.radiola.domain.repository.SettingsRepository
|
lateinit var settingsRepository: com.radiola.domain.repository.SettingsRepository
|
||||||
|
|
||||||
|
// После ответа на запрос уведомлений — просим исключение из оптимизации батареи.
|
||||||
|
private val notifPermLauncher = registerForActivityResult(
|
||||||
|
androidx.activity.result.contract.ActivityResultContracts.RequestPermission()
|
||||||
|
) { maybeRequestBatteryExemption() }
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -56,6 +62,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
tokenDataStore.preload()
|
tokenDataStore.preload()
|
||||||
}
|
}
|
||||||
|
ensureBackgroundPlaybackAllowed()
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
// Выбранная цветовая тема (мгновенно перекрашивает всё приложение).
|
// Выбранная цветовая тема (мгновенно перекрашивает всё приложение).
|
||||||
@@ -287,4 +294,39 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Условия для стабильного фонового воспроизведения:
|
||||||
|
* 1) POST_NOTIFICATIONS (Android 13+) — без него не видно медиа-уведомление и
|
||||||
|
* foreground-сервис легко убивается системой;
|
||||||
|
* 2) исключение из оптимизации батареи (Doze/ColorOS глушат фон).
|
||||||
|
*/
|
||||||
|
private fun ensureBackgroundPlaybackAllowed() {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 33 &&
|
||||||
|
checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) !=
|
||||||
|
android.content.pm.PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
// После ответа (в колбэке) попросим про батарею — не два диалога разом.
|
||||||
|
notifPermLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
} else {
|
||||||
|
maybeRequestBatteryExemption()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun maybeRequestBatteryExemption() {
|
||||||
|
val pm = getSystemService(Context.POWER_SERVICE) as android.os.PowerManager
|
||||||
|
if (pm.isIgnoringBatteryOptimizations(packageName)) return
|
||||||
|
// Спрашиваем один раз на установку, чтобы не надоедать.
|
||||||
|
val prefs = getSharedPreferences("radiola_prefs", MODE_PRIVATE)
|
||||||
|
if (prefs.getBoolean("battery_opt_asked", false)) return
|
||||||
|
prefs.edit().putBoolean("battery_opt_asked", true).apply()
|
||||||
|
runCatching {
|
||||||
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||||
|
android.net.Uri.parse("package:$packageName")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user