mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
android, desktop: sound prompts and vibration (#3997)
* android, desktop: sound prompts and vibration
* moved audio playing to js
* Revert "moved audio playing to js"
This reverts commit a9355c2d88.
* sound from speaker/earpiece
* better
* changes
* license of audio
* reorder lines
* loop fix
* awaiting call receipt
* desktop
* new line
This commit is contained in:
committed by
GitHub
parent
94851f177b
commit
34dee32def
+84
-6
@@ -2,17 +2,16 @@ 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 chat.simplex.common.views.helpers.withApi
|
||||
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 +31,7 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
scope.launch {
|
||||
while (playing) {
|
||||
if (sound) player?.start()
|
||||
vibrator?.vibrate(effect)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
delay(3500)
|
||||
}
|
||||
}
|
||||
@@ -43,3 +42,82 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
player?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
object CallSoundsPlayer: CallSoundsPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
private var playingJob: Job? = null
|
||||
|
||||
private fun start(soundPath: String, delay: Long, scope: CoroutineScope) {
|
||||
playingJob?.cancel()
|
||||
player?.reset()
|
||||
player = MediaPlayer().apply {
|
||||
setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
|
||||
.build()
|
||||
)
|
||||
setDataSource(androidAppContext, Uri.parse(soundPath))
|
||||
prepare()
|
||||
}
|
||||
if (delay < 1000) {
|
||||
player?.isLooping = true
|
||||
player?.start()
|
||||
return
|
||||
}
|
||||
playingJob = scope.launch {
|
||||
while (isActive) {
|
||||
player?.start()
|
||||
delay(delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startConnectingCallSound(scope: CoroutineScope) {
|
||||
// Taken from https://github.com/TelegramOrg/Telegram-Android
|
||||
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
|
||||
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.connecting_call, 0, scope)
|
||||
}
|
||||
|
||||
override fun startInCallSound(scope: CoroutineScope) {
|
||||
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.in_call, 2000, scope)
|
||||
}
|
||||
|
||||
override fun vibrate(times: Int) {
|
||||
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
|
||||
val effect = VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
repeat(times - 1) {
|
||||
withApi {
|
||||
delay(50)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
playingJob?.cancel()
|
||||
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 {
|
||||
|
||||
+17
-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,13 @@ actual fun ActiveCallView() {
|
||||
null
|
||||
}
|
||||
}
|
||||
val wasConnected = rememberSaveable { mutableStateOf(false) }
|
||||
LaunchedEffect(call) {
|
||||
if (call?.callState == CallState.Connected && !wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate(2)
|
||||
wasConnected.value = true
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
var btDeviceCount = 0
|
||||
@@ -107,6 +116,10 @@ actual fun ActiveCallView() {
|
||||
}
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
onDispose {
|
||||
CallSoundsPlayer.stop()
|
||||
if (wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate()
|
||||
}
|
||||
dropAudioManagerOverrides()
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
if (proximityLock?.isHeld == true) {
|
||||
@@ -122,8 +135,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")
|
||||
@@ -136,6 +147,9 @@ actual fun ActiveCallView() {
|
||||
val callType = CallType(call.localMedia, r.capabilities)
|
||||
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) }
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
CallSoundsPlayer.startConnectingCallSound(scope)
|
||||
activeCallWaitDeliveryReceipt(scope)
|
||||
}
|
||||
is WCallResponse.Offer -> withBGApi {
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities)
|
||||
@@ -144,6 +158,7 @@ actual fun ActiveCallView() {
|
||||
is WCallResponse.Answer -> withBGApi {
|
||||
chatModel.controller.apiSendCallAnswer(callRh, call.contact, r.answer, r.iceCandidates)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Negotiated) }
|
||||
CallSoundsPlayer.stop()
|
||||
}
|
||||
is WCallResponse.Ice -> withBGApi {
|
||||
chatModel.controller.apiSendCallExtraInfo(callRh, call.contact, r.iceCandidates)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user