diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe02f6db223..8723e2760b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,10 @@ Docs: https://docs.openclaw.ai
- LINE/Voice transcription: classify M4A voice media as `audio/mp4` (not `video/mp4`) by checking the MPEG-4 `ftyp` major brand (`M4A ` / `M4B `), restoring voice transcription for LINE voice messages. Landed from contributor PR #31151 by @scoootscooob. Thanks @scoootscooob.
- Slack/Announce target account routing: enable session-backed announce-target lookup for Slack so multi-account announces resolve the correct `accountId` instead of defaulting to bot-token context. Landed from contributor PR #31028 by @taw0002. Thanks @taw0002.
- Android/Voice screen TTS: stream assistant speech via ElevenLabs WebSocket in Talk Mode, stop cleanly on speaker mute/barge-in, and ignore stale out-of-order stream events. (#29521) Thanks @gregmousseau.
+- Android/Photos permissions: declare Android 14+ selected-photo access permission (`READ_MEDIA_VISUAL_USER_SELECTED`) and align Android permission/settings paths with current minSdk behavior for more reliable permission state handling.
+- Web UI/Cron: include configured agent model defaults/fallbacks in cron model suggestions so scheduled-job model autocomplete reflects configured models. (#29709) Thanks @Sid-Qin.
+- Cron/Delivery: disable the agent messaging tool when `delivery.mode` is `"none"` so cron output is not sent to Telegram or other channels. (#21808) Thanks @lailoo.
+- CLI/Cron: clarify `cron list` output by renaming `Agent` to `Agent ID` and adding a `Model` column for isolated agent-turn jobs. (#26259) Thanks @openperf.
- Feishu/Reply media attachments: send Feishu reply `mediaUrl`/`mediaUrls` payloads as attachments alongside text/streamed replies in the reply dispatcher, including legacy fallback when `mediaUrls` is empty. (#28959) Thanks @icesword0760.
- Slack/User-token resolution: normalize Slack account user-token sourcing through resolved account metadata (`SLACK_USER_TOKEN` env + config) so monitor reads, Slack actions, directory lookups, onboarding allow-from resolution, and capabilities probing consistently use the effective user token. (#28103) Thanks @Glucksberg.
- Feishu/Outbound session routing: stop assuming bare `oc_` identifiers are always group chats, honor explicit `dm:`/`group:` prefixes for `oc_` chat IDs, and default ambiguous bare `oc_` targets to direct routing to avoid DM session misclassification. (#10407) Thanks @Bermudarat.
@@ -118,7 +122,10 @@ Docs: https://docs.openclaw.ai
- Sandbox/mkdirp boundary checks: allow directory-safe boundary validation for existing in-boundary subdirectories, preventing false `cannot create directories` failures in sandbox write mode. (#30610) Thanks @glitch418x.
- Security/Compaction audit: remove the post-compaction audit injection message. (#28507) Thanks @fuller-stack-dev and @vincentkoc.
- Web tools/RFC2544 fake-IP compatibility: allow RFC2544 benchmark range (`198.18.0.0/15`) for trusted web-tool fetch endpoints so proxy fake-IP networking modes do not trigger false SSRF blocks. Landed from contributor PR #31176 by @sunkinux. Thanks @sunkinux.
-
+- Telegram/Voice fallback reply chunking: apply reply reference, quote text, and inline buttons only to the first fallback text chunk when voice delivery is blocked, preventing over-quoted multi-chunk replies. Landed from contributor PR #31067 by @xdanger. Thanks @xdanger.
+- Feishu/System preview prompt leakage: stop enqueuing inbound Feishu message previews as system events so user preview text is not injected into later turns as trusted `System:` context. Landed from contributor PR #31209 by @stakeswky. Thanks @stakeswky.
+- Feishu/Multi-account + reply reliability: add `channels.feishu.defaultAccount` outbound routing support with schema validation, keep quoted-message extraction text-first (post/interactive/file placeholders instead of raw JSON), route Feishu video sends as `msg_type: "file"`, and avoid websocket event blocking by using non-blocking event handling in monitor dispatch. Landed from contributor PRs #29610, #30432, #30331, and #29501. Thanks @hclsys, @bmendonca3, @patrick-yingxi-pan, and @zwffff.
+- Feishu/Typing replay suppression: skip typing indicators for stale replayed inbound messages after compaction using message-age checks with second/millisecond timestamp normalization, preventing old-message reaction floods while preserving typing for fresh messages. Landed from contributor PR #30709 by @arkyu2077. Thanks @arkyu2077.
## Unreleased
### Changes
diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts
index a2076b99cf6..0f0a78d51d6 100644
--- a/apps/android/app/build.gradle.kts
+++ b/apps/android/app/build.gradle.kts
@@ -66,6 +66,7 @@ android {
lint {
disable +=
setOf(
+ "AndroidGradlePluginVersion",
"GradleDependency",
"IconLauncherShape",
"NewerVersionAvailable",
diff --git a/apps/android/app/src/main/AndroidManifest.xml b/apps/android/app/src/main/AndroidManifest.xml
index f5e20fd5a97..0507bdf8aa1 100644
--- a/apps/android/app/src/main/AndroidManifest.xml
+++ b/apps/android/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt
index 0dc6f2b6955..87572b37ad8 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/CameraCaptureManager.kt
@@ -359,6 +359,7 @@ class CameraCaptureManager(private val context: Context) {
.build()
}
+ @SuppressLint("UnsafeOptInUsageError")
private fun cameraDeviceInfoOrNull(info: CameraInfo): CameraDeviceInfo? {
val cameraId = cameraIdOrNull(info) ?: return null
val lensFacing =
@@ -389,6 +390,7 @@ class CameraCaptureManager(private val context: Context) {
)
}
+ @SuppressLint("UnsafeOptInUsageError")
private fun cameraIdOrNull(info: CameraInfo): String? =
runCatching { Camera2CameraInfo.from(info).cameraId }.getOrNull()
}
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceHandler.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceHandler.kt
index a091b6f211b..4c7045b4608 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceHandler.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceHandler.kt
@@ -136,12 +136,7 @@ class DeviceHandler(
} else {
hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
}
- val motionGranted =
- if (Build.VERSION.SDK_INT >= 29) {
- hasPermission(Manifest.permission.ACTIVITY_RECOGNITION)
- } else {
- true
- }
+ val motionGranted = hasPermission(Manifest.permission.ACTIVITY_RECOGNITION)
val notificationsGranted =
if (Build.VERSION.SDK_INT >= 33) {
hasPermission(Manifest.permission.POST_NOTIFICATIONS)
@@ -228,7 +223,7 @@ class DeviceHandler(
"motion",
permissionStateJson(
granted = motionGranted,
- promptableWhenDenied = Build.VERSION.SDK_INT >= 29,
+ promptableWhenDenied = true,
),
)
// Screen capture on Android is interactive per-capture consent, not a sticky app permission.
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt
index 0663bb8158d..4a2ce7a9a78 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/DeviceNotificationListenerService.kt
@@ -6,7 +6,6 @@ import android.app.RemoteInput
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.os.Build
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import kotlinx.serialization.json.JsonPrimitive
@@ -234,9 +233,6 @@ class DeviceNotificationListenerService : NotificationListenerService() {
}
fun requestServiceRebind(context: Context) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
- return
- }
runCatching {
NotificationListenerService.requestRebind(serviceComponent(context))
}
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/MotionHandler.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/MotionHandler.kt
index d385b35f182..52658f8efb6 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/MotionHandler.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/MotionHandler.kt
@@ -6,7 +6,6 @@ import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
-import android.os.Build
import android.os.SystemClock
import androidx.core.content.ContextCompat
import ai.openclaw.android.gateway.GatewaySession
@@ -85,7 +84,6 @@ private object SystemMotionDataSource : MotionDataSource {
}
override fun hasPermission(context: Context): Boolean {
- if (Build.VERSION.SDK_INT < 29) return true
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACTIVITY_RECOGNITION) ==
android.content.pm.PackageManager.PERMISSION_GRANTED
}
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/PhotosHandler.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/PhotosHandler.kt
index 3b9c0199d06..e7f3debff06 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/PhotosHandler.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/PhotosHandler.kt
@@ -11,6 +11,7 @@ import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import androidx.core.content.ContextCompat
+import androidx.core.graphics.scale
import ai.openclaw.android.gateway.GatewaySession
import java.io.ByteArrayOutputStream
import java.time.Instant
@@ -158,7 +159,7 @@ private object SystemPhotosDataSource : PhotosDataSource {
if (decoded.width <= maxWidth) return decoded
val targetHeight = max(1, ((decoded.height.toDouble() * maxWidth) / decoded.width).roundToInt())
- return Bitmap.createScaledBitmap(decoded, maxWidth, targetHeight, true)
+ return decoded.scale(maxWidth, targetHeight, true)
}
private fun computeInSampleSize(width: Int, maxWidth: Int): Int {
@@ -198,7 +199,7 @@ private object SystemPhotosDataSource : PhotosDataSource {
val nextWidth = max(240, (working.width * 0.75f).roundToInt())
if (nextWidth >= working.width) return null
val nextHeight = max(1, ((working.height.toDouble() * nextWidth) / working.width).roundToInt())
- working = Bitmap.createScaledBitmap(working, nextWidth, nextHeight, true)
+ working = working.scale(nextWidth, nextHeight, true)
}
return null
}
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/node/SystemHandler.kt b/apps/android/app/src/main/java/ai/openclaw/android/node/SystemHandler.kt
index 6d8fd5b86aa..ee794f7ac4e 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/node/SystemHandler.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/node/SystemHandler.kt
@@ -67,9 +67,6 @@ private class AndroidSystemNotificationPoster(
}
private fun ensureChannel(priority: String?): String {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
- return NOTIFICATION_CHANNEL_BASE_ID
- }
val normalizedPriority = priority.orEmpty().trim().lowercase()
val (suffix, importance, name) =
when (normalizedPriority) {
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/ui/OnboardingFlow.kt b/apps/android/app/src/main/java/ai/openclaw/android/ui/OnboardingFlow.kt
index 1a25cce68d3..cc596706ec0 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/ui/OnboardingFlow.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/ui/OnboardingFlow.kt
@@ -80,6 +80,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
@@ -242,7 +243,7 @@ fun OnboardingFlow(viewModel: MainViewModel, modifier: Modifier = Modifier) {
remember(context) {
hasMotionCapabilities(context)
}
- val motionPermissionRequired = Build.VERSION.SDK_INT >= 29
+ val motionPermissionRequired = true
val notificationsPermissionRequired = Build.VERSION.SDK_INT >= 33
val discoveryPermission =
if (Build.VERSION.SDK_INT >= 33) {
@@ -1635,7 +1636,6 @@ private fun isNotificationListenerEnabled(context: Context): Boolean {
}
private fun canInstallUnknownApps(context: Context): Boolean {
- if (Build.VERSION.SDK_INT < 26) return true
return context.packageManager.canRequestPackageInstalls()
}
@@ -1649,11 +1649,10 @@ private fun openNotificationListenerSettings(context: Context) {
}
private fun openUnknownAppSourcesSettings(context: Context) {
- if (Build.VERSION.SDK_INT < 26) return
val intent =
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
- Uri.parse("package:${context.packageName}"),
+ "package:${context.packageName}".toUri(),
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
runCatching {
context.startActivity(intent)
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/ui/SettingsSheet.kt b/apps/android/app/src/main/java/ai/openclaw/android/ui/SettingsSheet.kt
index b20013f7d57..cd1368db1b4 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/ui/SettingsSheet.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/ui/SettingsSheet.kt
@@ -62,6 +62,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
+import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
@@ -171,7 +172,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
- val motionPermissionRequired = Build.VERSION.SDK_INT >= 29
+ val motionPermissionRequired = true
val motionAvailable = remember(context) { hasMotionCapabilities(context) }
var notificationsPermissionGranted by
@@ -424,7 +425,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Microphone permission", style = mobileHeadline) },
supportingContent = {
@@ -477,7 +478,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Allow Camera", style = mobileHeadline) },
supportingContent = { Text("Allows the gateway to request photos or short video clips (foreground only).", style = mobileCallout) },
@@ -510,7 +511,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
else -> "Grant"
}
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("SMS Permission", style = mobileHeadline) },
supportingContent = {
@@ -561,7 +562,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
"Grant"
}
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("System Notifications", style = mobileHeadline) },
supportingContent = {
@@ -589,7 +590,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Notification Listener Access", style = mobileHeadline) },
supportingContent = {
@@ -624,7 +625,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Photos Permission", style = mobileHeadline) },
supportingContent = {
@@ -655,7 +656,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Contacts Permission", style = mobileHeadline) },
supportingContent = {
@@ -686,7 +687,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Calendar Permission", style = mobileHeadline) },
supportingContent = {
@@ -724,7 +725,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
else -> "Grant"
}
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Motion Permission", style = mobileHeadline) },
supportingContent = {
@@ -768,7 +769,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Install App Updates", style = mobileHeadline) },
supportingContent = {
@@ -802,7 +803,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
)
}
item {
- Column(modifier = settingsRowModifier(), verticalArrangement = Arrangement.spacedBy(0.dp)) {
+ Column(modifier = Modifier.settingsRowModifier(), verticalArrangement = Arrangement.spacedBy(0.dp)) {
ListItem(
modifier = Modifier.fillMaxWidth(),
colors = listItemColors,
@@ -877,7 +878,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Prevent Sleep", style = mobileHeadline) },
supportingContent = { Text("Keeps the screen awake while OpenClaw is open.", style = mobileCallout) },
@@ -897,7 +898,7 @@ fun SettingsSheet(viewModel: MainViewModel) {
}
item {
ListItem(
- modifier = settingsRowModifier(),
+ modifier = Modifier.settingsRowModifier(),
colors = listItemColors,
headlineContent = { Text("Debug Canvas Status", style = mobileHeadline) },
supportingContent = { Text("Show status text in the canvas when debug is enabled.", style = mobileCallout) },
@@ -927,8 +928,8 @@ private fun settingsTextFieldColors() =
cursorColor = mobileAccent,
)
-private fun settingsRowModifier() =
- Modifier
+private fun Modifier.settingsRowModifier() =
+ this
.fillMaxWidth()
.border(width = 1.dp, color = mobileBorder, shape = RoundedCornerShape(14.dp))
.background(Color.White, RoundedCornerShape(14.dp))
@@ -970,14 +971,10 @@ private fun openNotificationListenerSettings(context: Context) {
}
private fun openUnknownAppSourcesSettings(context: Context) {
- if (Build.VERSION.SDK_INT < 26) {
- openAppSettings(context)
- return
- }
val intent =
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
- Uri.parse("package:${context.packageName}"),
+ "package:${context.packageName}".toUri(),
)
runCatching {
context.startActivity(intent)
@@ -997,7 +994,6 @@ private fun isNotificationListenerEnabled(context: Context): Boolean {
}
private fun canInstallUnknownApps(context: Context): Boolean {
- if (Build.VERSION.SDK_INT < 26) return true
return context.packageManager.canRequestPackageInstalls()
}
diff --git a/apps/android/app/src/main/java/ai/openclaw/android/voice/TalkModeManager.kt b/apps/android/app/src/main/java/ai/openclaw/android/voice/TalkModeManager.kt
index 8bafd603b85..3b20b4f5429 100644
--- a/apps/android/app/src/main/java/ai/openclaw/android/voice/TalkModeManager.kt
+++ b/apps/android/app/src/main/java/ai/openclaw/android/voice/TalkModeManager.kt
@@ -24,7 +24,6 @@ import androidx.core.content.ContextCompat
import ai.openclaw.android.gateway.GatewaySession
import ai.openclaw.android.isCanonicalMainSessionKey
import ai.openclaw.android.normalizeMainKey
-import android.os.Build
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
@@ -1316,43 +1315,28 @@ private const val defaultTalkProvider = "elevenlabs"
private fun requestAudioFocusForTts(): Boolean {
val am = context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: return true
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val req = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
- .setAudioAttributes(
- AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
- .build()
- )
- .setOnAudioFocusChangeListener(audioFocusListener)
- .build()
- audioFocusRequest = req
- val result = am.requestAudioFocus(req)
- Log.d(tag, "audio focus request result=$result")
- result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED || result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED
- } else {
- @Suppress("DEPRECATION")
- val result = am.requestAudioFocus(
- audioFocusListener,
- AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+ val req = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK)
+ .setAudioAttributes(
+ AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ .build()
)
- result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
- }
+ .setOnAudioFocusChangeListener(audioFocusListener)
+ .build()
+ audioFocusRequest = req
+ val result = am.requestAudioFocus(req)
+ Log.d(tag, "audio focus request result=$result")
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED || result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED
}
private fun abandonAudioFocus() {
val am = context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: return
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- audioFocusRequest?.let {
- am.abandonAudioFocusRequest(it)
- Log.d(tag, "audio focus abandoned")
- }
- audioFocusRequest = null
- } else {
- @Suppress("DEPRECATION")
- am.abandonAudioFocus(audioFocusListener)
+ audioFocusRequest?.let {
+ am.abandonAudioFocusRequest(it)
+ Log.d(tag, "audio focus abandoned")
}
+ audioFocusRequest = null
}
private fun cleanupPlayer() {