fix: краш «Свой звук» на FragmentActivity (0.5.5)

Ringtone picker через startActivityForResult вместо Activity Result API с большим requestCode.
This commit is contained in:
PTah
2026-06-14 18:32:44 +10:00
parent 902e47880d
commit 37c8357a87
5 changed files with 79 additions and 43 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ android {
applicationId = "ru.kalinamall.seaca" applicationId = "ru.kalinamall.seaca"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 18 versionCode = 19
versionName = "0.5.4" versionName = "0.5.5"
} }
buildTypes { buildTypes {
@@ -59,6 +59,7 @@ import ru.kalinamall.seaca.ui.screens.HostDetailScreen
import ru.kalinamall.seaca.ui.screens.HostsScreen import ru.kalinamall.seaca.ui.screens.HostsScreen
import ru.kalinamall.seaca.ui.screens.ProblemDetailScreen import ru.kalinamall.seaca.ui.screens.ProblemDetailScreen
import ru.kalinamall.seaca.ui.screens.ProblemsScreen import ru.kalinamall.seaca.ui.screens.ProblemsScreen
import ru.kalinamall.seaca.ui.RingtonePicker
import ru.kalinamall.seaca.ui.screens.ReportsScreen import ru.kalinamall.seaca.ui.screens.ReportsScreen
import ru.kalinamall.seaca.ui.theme.SeacaTheme import ru.kalinamall.seaca.ui.theme.SeacaTheme
@@ -94,6 +95,13 @@ class MainActivity : FragmentActivity() {
deepLinkState.value = extractDeepLink(intent) deepLinkState.value = extractDeepLink(intent)
} }
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@Suppress("DEPRECATION")
super.onActivityResult(requestCode, resultCode, data)
RingtonePicker.handleActivityResult(requestCode, resultCode, data)
}
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
if (shouldRequireBiometric()) { if (shouldRequireBiometric()) {
@@ -20,7 +20,11 @@ object NotificationSoundResolver {
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
fun displayName(context: Context, settings: NotificationSoundSettings): String { fun displayName(context: Context, settings: NotificationSoundSettings): String {
val uri = resolve(context, settings) return try {
return RingtoneManager.getRingtone(context, uri)?.getTitle(context) ?: "" val uri = resolve(context, settings)
RingtoneManager.getRingtone(context, uri)?.getTitle(context) ?: ""
} catch (_: Exception) {
""
}
} }
} }
@@ -0,0 +1,52 @@
package ru.kalinamall.seaca.ui
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import androidx.fragment.app.FragmentActivity
import ru.kalinamall.seaca.push.NotificationSoundResolver
object RingtonePicker {
private const val REQUEST_CODE = 9001
private var callback: ((Uri?) -> Unit)? = null
fun launch(activity: FragmentActivity, currentUri: Uri, onResult: (Uri?) -> Unit) {
callback = onResult
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, NotificationSoundResolver.defaultNotificationUri())
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentUri)
}
try {
@Suppress("DEPRECATION")
activity.startActivityForResult(intent, REQUEST_CODE)
} catch (_: ActivityNotFoundException) {
callback = null
throw ActivityNotFoundException()
}
}
fun handleActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != REQUEST_CODE) return
val onResult = callback ?: return
callback = null
val uri = if (resultCode == Activity.RESULT_OK) readResult(data) else null
onResult(uri)
}
private fun readResult(data: Intent?): Uri? {
if (data == null) return null
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)
} else {
@Suppress("DEPRECATION")
data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
}
}
@@ -1,16 +1,10 @@
package ru.kalinamall.seaca.ui.screens package ru.kalinamall.seaca.ui.screens
import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent
import android.widget.Toast import android.widget.Toast
import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.rememberUpdatedState
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -64,6 +58,7 @@ import ru.kalinamall.seaca.data.ProblemDetail
import ru.kalinamall.seaca.data.SacRepository import ru.kalinamall.seaca.data.SacRepository
import ru.kalinamall.seaca.push.NotificationHelper import ru.kalinamall.seaca.push.NotificationHelper
import ru.kalinamall.seaca.push.NotificationSoundResolver import ru.kalinamall.seaca.push.NotificationSoundResolver
import ru.kalinamall.seaca.ui.RingtonePicker
import ru.kalinamall.seaca.ui.components.BackArrowButton import ru.kalinamall.seaca.ui.components.BackArrowButton
import ru.kalinamall.seaca.ui.components.ForwardArrowButton import ru.kalinamall.seaca.ui.components.ForwardArrowButton
import ru.kalinamall.seaca.ui.util.SacDateTimes import ru.kalinamall.seaca.ui.util.SacDateTimes
@@ -459,36 +454,23 @@ fun DeviceScreen(repo: SacRepository, onLogout: () -> Unit) {
} }
} }
val ringtonePickerLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult(),
) { result ->
if (result.resultCode != Activity.RESULT_OK) return@rememberLauncherForActivityResult
val uri = readRingtonePickerResult(result.data)
scope.launch {
if (uri == null) {
repo.session.setNotificationSound(NotificationSoundSource.SYSTEM, null)
} else {
repo.session.setNotificationSound(NotificationSoundSource.CUSTOM, uri.toString())
}
applySoundChannels(repo.session.getNotificationSoundSettings())
}
}
fun openRingtonePicker() { fun openRingtonePicker() {
if (activity == null) { if (activity == null) {
Toast.makeText(context, "Не удалось открыть выбор звука", Toast.LENGTH_SHORT).show() Toast.makeText(context, "Не удалось открыть выбор звука", Toast.LENGTH_SHORT).show()
return return
} }
val currentUri = NotificationSoundResolver.resolve(context, latestSoundSettings) val currentUri = NotificationSoundResolver.resolve(context, latestSoundSettings)
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION)
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, NotificationSoundResolver.defaultNotificationUri())
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentUri)
}
try { try {
ringtonePickerLauncher.launch(intent) RingtonePicker.launch(activity, currentUri) { uri ->
scope.launch {
if (uri == null) {
repo.session.setNotificationSound(NotificationSoundSource.SYSTEM, null)
} else {
repo.session.setNotificationSound(NotificationSoundSource.CUSTOM, uri.toString())
}
applySoundChannels(repo.session.getNotificationSoundSettings())
}
}
} catch (_: ActivityNotFoundException) { } catch (_: ActivityNotFoundException) {
Toast.makeText(context, "На устройстве нет выбора звука уведомлений", Toast.LENGTH_LONG).show() Toast.makeText(context, "На устройстве нет выбора звука уведомлений", Toast.LENGTH_LONG).show()
} }
@@ -730,14 +712,4 @@ private fun Field(label: String, value: String) {
} }
} }
private fun readRingtonePickerResult(data: Intent?): Uri? {
if (data == null) return null
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)
} else {
@Suppress("DEPRECATION")
data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
}
}
private fun JsonElement.toPrettyString(): String = toString() private fun JsonElement.toPrettyString(): String = toString()