diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java index 19e075ec6a..ba6fdbc4db 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupConnectedActionProcessor.java @@ -259,6 +259,8 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { @Override protected @NonNull WebRtcServiceState handleGroupCallRaisedHand(@NonNull WebRtcServiceState currentState, List raisedHands) { Log.i(TAG, "handleGroupCallRaisedHand():"); + + boolean playSound = false; long now = System.currentTimeMillis(); WebRtcServiceStateBuilder.CallInfoStateBuilder builder = currentState.builder().changeCallInfoState(); Long localDemuxId = currentState.getCallInfoState().requireGroupCall().getLocalDeviceState().getDemuxId(); @@ -270,6 +272,7 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { boolean wasHandAlreadyRaised = updatedParticipant.isHandRaised(); if (raisedHandIndex >= 0 && !wasHandAlreadyRaised) { builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(now + raisedHandIndex)); + playSound = true; } else if (raisedHandIndex < 0 && wasHandAlreadyRaised) { builder.putParticipant(updatedParticipant.getCallParticipantId(), updatedParticipant.withHandRaisedTimestamp(CallParticipant.HAND_LOWERED)); } @@ -290,6 +293,10 @@ public class GroupConnectedActionProcessor extends GroupActionProcessor { new CallParticipantId(localDemuxId, Recipient.self().getId()))); } } + if (playSound) { + webRtcInteractor.playStateChangeUp(); + } + return builder.build(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java index 8e53b1785a..8091a92ee4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/WebRtcInteractor.java @@ -165,6 +165,10 @@ public class WebRtcInteractor { WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.SetDefaultDevice(recipientId, userDevice, clearUserEarpieceSelection)); } + public void playStateChangeUp() { + WebRtcCallService.sendAudioManagerCommand(context, new AudioManagerCommand.PlayStateChangeUp()); + } + void peekGroupCallForRingingCheck(@NonNull GroupCallRingCheckInfo groupCallRingCheckInfo) { signalCallManager.peekGroupCallForRingingCheck(groupCallRingCheckInfo); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt index d97dda3371..c99acb7e9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCommand.kt @@ -115,6 +115,13 @@ sealed class AudioManagerCommand : Parcelable { } } + class PlayStateChangeUp : AudioManagerCommand() { + companion object { + @JvmField + val CREATOR: Parcelable.Creator = ParcelCheat { PlayStateChangeUp() } + } + } + class ParcelCheat(private val createFrom: (Parcel) -> T) : Parcelable.Creator { override fun createFromParcel(parcel: Parcel): T = createFrom(parcel) override fun newArray(size: Int): Array = throw UnsupportedOperationException() diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java index ae9f85455a..f69eff5ab4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/AudioManagerCompat.java @@ -191,6 +191,10 @@ public abstract class AudioManagerCompat { return (float) (1 - (Math.log(maxVolume + 1 - volume) / Math.log(maxVolume + 1))); } + public float getVoiceCallVolume() { + return audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + } + abstract public SoundPool createSoundPool(); abstract public boolean requestCallAudioFocus(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt index deac5b868e..f3f5400a9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalAudioManager.kt @@ -44,6 +44,10 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev protected val incomingRinger = IncomingRinger(context) protected val outgoingRinger = OutgoingRinger(context) + private val stateChangeUpSoundId = context.assets.openFd("sounds/state-change_confirm-up.ogg").use { + soundPool.load(it, 1) + } + companion object { @JvmStatic fun create(context: Context, eventListener: EventListener?): SignalAudioManager { @@ -66,6 +70,7 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev is AudioManagerCommand.StartIncomingRinger -> startIncomingRinger(command.ringtoneUri, command.vibrate) is AudioManagerCommand.SilenceIncomingRinger -> silenceIncomingRinger() is AudioManagerCommand.StartOutgoingRinger -> startOutgoingRinger() + is AudioManagerCommand.PlayStateChangeUp -> playStateChangeUp() } } } @@ -81,6 +86,11 @@ sealed class SignalAudioManager(protected val context: Context, protected val ev } } + private fun playStateChangeUp() { + val volume: Float = androidAudioManager.voiceCallVolume + soundPool.play(stateChangeUpSoundId, volume, volume, 0, 0, 1f) + } + protected abstract fun initialize() protected abstract fun start() protected abstract fun stop(playDisconnect: Boolean)