mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-28 12:09:41 +00:00
android, desktop: sound prompts and vibration
This commit is contained in:
+62
-6
@@ -2,17 +2,15 @@ package chat.simplex.common.helpers
|
||||
|
||||
import android.media.*
|
||||
import android.net.Uri
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.os.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import chat.simplex.common.R
|
||||
import chat.simplex.common.platform.SoundPlayerInterface
|
||||
import chat.simplex.common.platform.androidAppContext
|
||||
import chat.simplex.common.platform.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
object SoundPlayer: SoundPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
var playing = false
|
||||
private var playing = false
|
||||
|
||||
override fun start(scope: CoroutineScope, sound: Boolean) {
|
||||
player?.reset()
|
||||
@@ -32,7 +30,7 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
scope.launch {
|
||||
while (playing) {
|
||||
if (sound) player?.start()
|
||||
vibrator?.vibrate(effect)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
delay(3500)
|
||||
}
|
||||
}
|
||||
@@ -43,3 +41,61 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
player?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
object CallSoundsPlayer: CallSoundsPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
private var playing = false
|
||||
|
||||
override fun startWaitingAnswerSound(scope: CoroutineScope) {
|
||||
player?.reset()
|
||||
player = MediaPlayer().apply {
|
||||
setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
|
||||
.build()
|
||||
)
|
||||
setDataSource(androidAppContext, Uri.parse("android.resource://" + androidAppContext.packageName + "/" + R.raw.call_sound_before_answer))
|
||||
prepare()
|
||||
}
|
||||
playing = true
|
||||
scope.launch {
|
||||
while (playing) {
|
||||
player?.start()
|
||||
delay(2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun vibrate() {
|
||||
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
|
||||
val effect = VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
playing = false
|
||||
player?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Vibrator.vibrateApiVersionAware(effect: VibrationEffect) {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
vibrate(
|
||||
effect,
|
||||
VibrationAttributes.Builder()
|
||||
.setUsage(VibrationAttributes.USAGE_ALARM)
|
||||
.build()
|
||||
)
|
||||
} else if (Build.VERSION.SDK_INT >= 29) {
|
||||
vibrate(
|
||||
effect,
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_ALARM)
|
||||
.build()
|
||||
)
|
||||
} else {
|
||||
vibrate(effect)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -296,6 +296,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
|
||||
actual typealias SoundPlayer = chat.simplex.common.helpers.SoundPlayer
|
||||
actual typealias CallSoundsPlayer = chat.simplex.common.helpers.CallSoundsPlayer
|
||||
|
||||
class CryptoMediaSource(val data: ByteArray) : MediaDataSource() {
|
||||
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
|
||||
|
||||
+20
-2
@@ -69,6 +69,8 @@ fun activeCallDestroyWebView() = withApi {
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
@Composable
|
||||
actual fun ActiveCallView() {
|
||||
val call = remember { chatModel.activeCall }.value
|
||||
val scope = rememberCoroutineScope()
|
||||
val audioViaBluetooth = rememberSaveable { mutableStateOf(false) }
|
||||
val proximityLock = remember {
|
||||
val pm = (androidAppContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
@@ -78,6 +80,16 @@ actual fun ActiveCallView() {
|
||||
null
|
||||
}
|
||||
}
|
||||
val wasConnected = rememberSaveable { mutableStateOf(false) }
|
||||
LaunchedEffect(call) {
|
||||
if (call?.callState == CallState.Connected && !wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate()
|
||||
wasConnected.value = true
|
||||
}
|
||||
if ((call?.callState ?: CallState.WaitCapabilities) >= CallState.OfferReceived) {
|
||||
CallSoundsPlayer.stop()
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
var btDeviceCount = 0
|
||||
@@ -106,7 +118,15 @@ actual fun ActiveCallView() {
|
||||
}
|
||||
}
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
if (call?.callState == CallState.WaitCapabilities || call?.callState == CallState.InvitationSent) {
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
CallSoundsPlayer.startWaitingAnswerSound(scope)
|
||||
}
|
||||
onDispose {
|
||||
CallSoundsPlayer.stop()
|
||||
if (wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate()
|
||||
}
|
||||
dropAudioManagerOverrides()
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
if (proximityLock?.isHeld == true) {
|
||||
@@ -122,8 +142,6 @@ actual fun ActiveCallView() {
|
||||
if (proximityLock?.isHeld == false) proximityLock.acquire()
|
||||
}
|
||||
}
|
||||
val scope = rememberCoroutineScope()
|
||||
val call = chatModel.activeCall.value
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
WebRTCView(chatModel.callCommand) { apiMsg ->
|
||||
Log.d(TAG, "received from WebRTCView: $apiMsg")
|
||||
|
||||
Binary file not shown.
+8
@@ -39,4 +39,12 @@ interface SoundPlayerInterface {
|
||||
fun stop()
|
||||
}
|
||||
|
||||
interface CallSoundsPlayerInterface {
|
||||
fun startWaitingAnswerSound(scope: CoroutineScope)
|
||||
fun stop()
|
||||
fun vibrate()
|
||||
}
|
||||
|
||||
expect object SoundPlayer: SoundPlayerInterface
|
||||
|
||||
expect object CallSoundsPlayer: CallSoundsPlayerInterface
|
||||
|
||||
+32
@@ -228,3 +228,35 @@ actual object SoundPlayer: SoundPlayerInterface {
|
||||
AudioPlayer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
actual object CallSoundsPlayer: CallSoundsPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
private var playing = false
|
||||
|
||||
override fun startWaitingAnswerSound(scope: CoroutineScope) {
|
||||
val tmpFile = File(tmpDir, UUID.randomUUID().toString())
|
||||
tmpFile.deleteOnExit()
|
||||
SoundPlayer::class.java.getResource("/media/call_sound_before_answer.mp3").openStream()!!.use { it.copyTo(tmpFile.outputStream()) }
|
||||
SoundPlayer.playing = true
|
||||
scope.launch {
|
||||
while (SoundPlayer.playing) {
|
||||
AudioPlayer.play(CryptoFile.plain(tmpFile.absolutePath), mutableStateOf(true), mutableStateOf(0), mutableStateOf(0), true)
|
||||
delay(3500)
|
||||
}
|
||||
}
|
||||
playing = true
|
||||
scope.launch {
|
||||
while (playing) {
|
||||
player?.start()
|
||||
delay(3500)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun vibrate() {}
|
||||
|
||||
override fun stop() {
|
||||
playing = false
|
||||
AudioPlayer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user