diff --git a/app/build.gradle b/app/build.gradle index 6c07f551d3..e952e88e5e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -155,8 +155,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.4' } -def canonicalVersionCode = 236 -def canonicalVersionName = "1.12.0-ALPHA3" +def canonicalVersionCode = 237 +def canonicalVersionName = "1.12.0-ALPHA4" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, @@ -246,6 +246,7 @@ android { buildTypes { release { signingConfig signingConfigs.release + debuggable true minifyEnabled false } debug { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt index 5d6d0319fe..19239cff69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.kt @@ -176,6 +176,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { @Synchronized private fun terminate() { sendBroadcast(Intent(WebRtcCallActivity.ACTION_END)) + lockManager.updatePhoneState(LockManager.PhoneState.IDLE) callManager.stop() stopForeground(true) } @@ -377,7 +378,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING) setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient) callManager.insertCallMessage(recipient.address.serialize(), CallMessageType.CALL_OUTGOING) - timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES) + timeoutExecutor.schedule(TimeoutRunnable(callId, this), 1, TimeUnit.MINUTES) val expectedState = callManager.currentConnectionState val expectedCallId = callManager.callId @@ -425,7 +426,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { return } - timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES) + timeoutExecutor.schedule(TimeoutRunnable(callId, this), 1, TimeUnit.MINUTES) callManager.initializeAudioForCall() callManager.initializeVideo(this) @@ -490,11 +491,6 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { callManager.handleSetCameraFlip() } - private fun handleBluetoothChange(intent: Intent) { - val bluetoothAvailable = intent.getBooleanExtra(EXTRA_AVAILABLE, false) - callManager.postBluetoothAvailable(bluetoothAvailable) - } - private fun handleWiredHeadsetChanged(intent: Intent) { callManager.handleWiredHeadsetChanged(intent.getBooleanExtra(EXTRA_AVAILABLE, false)) } @@ -611,6 +607,7 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { System.currentTimeMillis() - intent.getLongExtra(EXTRA_TIMESTAMP, -1) > TimeUnit.MINUTES.toMillis(2) override fun onDestroy() { + Log.d(TAG,"onDestroy()") callManager.unregisterListener(this) callReceiver?.let { receiver -> unregisterReceiver(receiver) @@ -621,7 +618,6 @@ class WebRtcCallService: Service(), CallManager.WebRtcListener { networkChangedReceiver = null callReceiver = null uncaughtExceptionHandlerManager?.unregister() - callManager.onDestroy() super.onDestroy() // shutdown audiomanager // unregister network receiver diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt index 182b21c4ec..a035d0440b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/AudioManagerCommand.kt @@ -10,9 +10,6 @@ open class AudioManagerCommand: Parcelable { @Parcelize object Initialize: AudioManagerCommand() - @Parcelize - object Shutdown: AudioManagerCommand() - @Parcelize object UpdateAudioDeviceState: AudioManagerCommand() diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt index 3f11d10565..f9b76421cb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/CallManager.kt @@ -309,8 +309,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va } else if (json.containsKey("hangup")) { peerConnectionObservers.forEach(WebRtcListener::onHangup) } - val videoEnabled = Json.decodeFromString(VideoEnabledMessage.serializer(), byteArray.decodeToString()) - _remoteVideoEvents.value = VideoEnabled(videoEnabled.video) } catch (e: Exception) { Log.e(TAG, "Failed to deserialize data channel message", e) } @@ -519,7 +517,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va fun handleSetMuteVideo(muted: Boolean, lockManager: LockManager) { _videoEvents.value = VideoEnabled(!muted) - peerConnection?.setVideoEnabled(!muted) + val connection = peerConnection ?: return + connection.setVideoEnabled(!muted) dataChannel?.let { channel -> val toSend = if (muted) VIDEO_DISABLED_JSON else VIDEO_ENABLED_JSON val buffer = DataChannel.Buffer(ByteBuffer.wrap(toSend.toString().encodeToByteArray()), false) @@ -527,7 +526,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va } if (currentConnectionState == CallState.STATE_CONNECTED) { - if (localCameraState.enabled) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO) + if (connection.isVideoEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO) else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL) } @@ -548,10 +547,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va } } - fun postBluetoothAvailable(available: Boolean) { - // TODO: _bluetoothEnabled.value = available - } - fun handleWiredHeadsetChanged(present: Boolean) { if (currentConnectionState in arrayOf(CallState.STATE_CONNECTED, CallState.STATE_DIALING, @@ -601,10 +596,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va } } - fun onDestroy() { - signalAudioManager.handleCommand(AudioManagerCommand.Shutdown) - } - fun startIncomingRinger() { signalAudioManager.handleCommand(AudioManagerCommand.StartIncomingRinger(true)) } @@ -612,7 +603,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va fun startCommunication(lockManager: LockManager) { signalAudioManager.handleCommand(AudioManagerCommand.Start) val connection = peerConnection ?: return - if (localCameraState.enabled) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO) + if (connection.isVideoEnabled()) lockManager.updatePhoneState(LockManager.PhoneState.IN_VIDEO) else lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL) connection.setCommunicationMode() setAudioEnabled(true) @@ -649,9 +640,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va } } - @Serializable - data class VideoEnabledMessage(val video: Boolean) - interface WebRtcListener: PeerConnection.Observer { fun onHangup() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt index bd328eddda..ad383df809 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/PeerConnectionWrapper.kt @@ -24,7 +24,7 @@ class PeerConnectionWrapper(context: Context, private val videoTrack: VideoTrack? val readyForIce - get() = peerConnection.localDescription != null && peerConnection.remoteDescription != null + get() = peerConnection.localDescription != null && peerConnection.remoteDescription != null init { val turn = PeerConnection.IceServer.builder("turn:freyr.getsession.org").setUsername("session").setPassword("session").createIceServer() @@ -269,6 +269,8 @@ class PeerConnectionWrapper(context: Context, } } + fun isVideoEnabled() = camera.enabled + fun flipCamera() { camera.flip() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt index bacc7a0598..31b3bdf101 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/IncomingRinger.kt @@ -17,6 +17,9 @@ class IncomingRinger(private val context: Context) { private val vibrator: Vibrator? = ServiceUtil.getVibrator(context) var mediaPlayer: MediaPlayer? = null + val isRinging: Boolean + get() = mediaPlayer?.isPlaying ?: false + fun start(vibrate: Boolean) { val audioManager = ServiceUtil.getAudioManager(context) mediaPlayer?.release() @@ -31,16 +34,14 @@ class IncomingRinger(private val context: Context) { } mediaPlayer?.let { player -> - if (ringerMode == AudioManager.RINGER_MODE_NORMAL) { - try { - if (!player.isPlaying) { - player.prepare() - player.start() - Log.i(TAG,"Playing ringtone") - } - } catch (e: Exception) { - Log.e(TAG,"Failed to start mediaPlayer", e) + try { + if (!player.isPlaying) { + player.prepare() + player.start() + Log.i(TAG,"Playing ringtone") } + } catch (e: Exception) { + Log.e(TAG,"Failed to start mediaPlayer", e) } } ?: run { Log.w(TAG,"Not ringing, mediaPlayer: ${mediaPlayer?.let{"available"}}, mode: $ringerMode") 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 b8b0f4a0c4..2b4d34807c 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 @@ -11,6 +11,7 @@ import android.os.HandlerThread import network.loki.messenger.R import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.webrtc.AudioManagerCommand +import org.thoughtcrime.securesms.webrtc.audio.SignalBluetoothManager.State as BState private val TAG = Log.tag(SignalAudioManager::class.java) @@ -68,7 +69,6 @@ class SignalAudioManager(private val context: Context, } handler?.post { when (command) { - is AudioManagerCommand.Shutdown -> shutdown() is AudioManagerCommand.UpdateAudioDeviceState -> updateAudioDeviceState() is AudioManagerCommand.Start -> start() is AudioManagerCommand.Stop -> stop(command.playDisconnect) @@ -144,15 +144,23 @@ class SignalAudioManager(private val context: Context, return } - incomingRinger.stop() - outgoingRinger.stop() + handler?.post { + incomingRinger.stop() + outgoingRinger.stop() + stop(false) + if (commandAndControlThread != null) { + Log.i(TAG, "Shutting down command and control") + commandAndControlThread?.quitSafely() + commandAndControlThread = null + } + } if (playDisconnect) { val volume: Float = androidAudioManager.ringVolumeWithMinimum() soundPool.play(disconnectedSoundId, volume, volume, 0, 0, 1.0f) } - state = State.PREINITIALIZED + state = State.UNINITIALIZED wiredHeadsetReceiver?.let { receiver -> try { @@ -175,19 +183,6 @@ class SignalAudioManager(private val context: Context, Log.d(TAG, "Stopped") } - private fun shutdown() { - handler?.post { - incomingRinger.stop() - outgoingRinger.stop() - stop(false) - if (commandAndControlThread != null) { - Log.i(TAG, "Shutting down command and control") - commandAndControlThread?.quitSafely() - commandAndControlThread = null - } - } - } - private fun updateAudioDeviceState() { handler!!.assertHandlerThread() @@ -223,7 +218,7 @@ class SignalAudioManager(private val context: Context, var audioDeviceSetUpdated = audioDevices != newAudioDevices audioDevices = newAudioDevices - if (signalBluetoothManager!!.state == SignalBluetoothManager.State.UNAVAILABLE && userSelectedAudioDevice == AudioDevice.BLUETOOTH) { + if (signalBluetoothManager!!.state == BState.UNAVAILABLE && userSelectedAudioDevice == AudioDevice.BLUETOOTH) { userSelectedAudioDevice = AudioDevice.NONE } @@ -236,13 +231,14 @@ class SignalAudioManager(private val context: Context, userSelectedAudioDevice = AudioDevice.NONE } - val needBluetoothAudioStart = signalBluetoothManager!!.state == SignalBluetoothManager.State.AVAILABLE && signalBluetoothManager!!.state != SignalBluetoothManager.State.CONNECTING + val btState = signalBluetoothManager!!.state + val needBluetoothAudioStart = btState == BState.AVAILABLE && (userSelectedAudioDevice == AudioDevice.NONE || userSelectedAudioDevice == AudioDevice.BLUETOOTH || autoSwitchToBluetooth) - val needBluetoothAudioStop = (signalBluetoothManager!!.state == SignalBluetoothManager.State.CONNECTED || signalBluetoothManager!!.state != SignalBluetoothManager.State.CONNECTING) && + val needBluetoothAudioStop = (btState == BState.CONNECTED || btState == BState.CONNECTING) && (userSelectedAudioDevice != AudioDevice.NONE && userSelectedAudioDevice != AudioDevice.BLUETOOTH) - if (signalBluetoothManager!!.state.hasDevice()) { + if (btState.hasDevice()) { Log.i(TAG, "Need bluetooth audio: state: ${signalBluetoothManager!!.state} start: $needBluetoothAudioStart stop: $needBluetoothAudioStop") } @@ -251,18 +247,19 @@ class SignalAudioManager(private val context: Context, signalBluetoothManager!!.updateDevice() } - if (!autoSwitchToBluetooth && signalBluetoothManager!!.state == SignalBluetoothManager.State.UNAVAILABLE) { + if (!autoSwitchToBluetooth && signalBluetoothManager!!.state == BState.UNAVAILABLE) { autoSwitchToBluetooth = true } if (needBluetoothAudioStart && !needBluetoothAudioStop) { if (!signalBluetoothManager!!.startScoAudio()) { + Log.e(TAG,"Failed to start sco audio") audioDevices.remove(AudioDevice.BLUETOOTH) audioDeviceSetUpdated = true } } - if (autoSwitchToBluetooth && signalBluetoothManager!!.state == SignalBluetoothManager.State.CONNECTED) { + if (autoSwitchToBluetooth && signalBluetoothManager!!.state == BState.CONNECTED) { userSelectedAudioDevice = AudioDevice.BLUETOOTH autoSwitchToBluetooth = false } @@ -342,7 +339,6 @@ class SignalAudioManager(private val context: Context, private fun startIncomingRinger(vibrate: Boolean) { Log.i(TAG, "startIncomingRinger(): vibrate: $vibrate") androidAudioManager.mode = AudioManager.MODE_RINGTONE - setDefaultAudioDevice(AudioDevice.SPEAKER_PHONE, false) incomingRinger.start(vibrate) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalBluetoothManager.kt b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalBluetoothManager.kt index d90814977b..84a36ee821 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalBluetoothManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/webrtc/audio/SignalBluetoothManager.kt @@ -9,6 +9,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.media.AudioManager import org.session.libsignal.utilities.Log import org.thoughtcrime.securesms.webrtc.AudioManagerCommand import java.util.concurrent.TimeUnit @@ -73,7 +74,7 @@ class SignalBluetoothManager( val bluetoothHeadsetFilter = IntentFilter().apply { addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED) - addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED) + addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED) } bluetoothReceiver = BluetoothHeadsetBroadcastReceiver() @@ -134,7 +135,6 @@ class SignalBluetoothManager( state = State.CONNECTING androidAudioManager.startBluetoothSco() - androidAudioManager.isBluetoothScoOn = true scoConnectionAttempts++ startTimer() @@ -165,7 +165,7 @@ class SignalBluetoothManager( return } - if (bluetoothAdapter!!.getProfileConnectionState(BluetoothProfile.HEADSET) !in arrayOf(BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED)) { + if (bluetoothAdapter!!.getProfileConnectionState(BluetoothProfile.HEADSET) !in arrayOf(BluetoothProfile.STATE_CONNECTED)) { state = State.UNAVAILABLE Log.i(TAG, "No connected bluetooth headset") } else { @@ -216,6 +216,7 @@ class SignalBluetoothManager( private fun onServiceConnected(proxy: BluetoothHeadset?) { bluetoothHeadset = proxy + androidAudioManager.isBluetoothScoOn = true updateAudioDeviceState() } @@ -244,9 +245,9 @@ class SignalBluetoothManager( private fun onAudioStateChanged(audioState: Int, isInitialStateChange: Boolean) { Log.i(TAG, "onAudioStateChanged: state: $state audioState: ${audioState.toStateString()} initialSticky: $isInitialStateChange") - if (audioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { + if (audioState == AudioManager.SCO_AUDIO_STATE_CONNECTED) { cancelTimer() - if (state === State.CONNECTING) { + if (state == State.CONNECTING) { Log.d(TAG, "Bluetooth audio SCO is now connected") state = State.CONNECTED scoConnectionAttempts = 0 @@ -254,9 +255,9 @@ class SignalBluetoothManager( } else { Log.w(TAG, "Unexpected state ${audioState.toStateString()}") } - } else if (audioState == BluetoothHeadset.STATE_AUDIO_CONNECTING) { + } else if (audioState == AudioManager.SCO_AUDIO_STATE_CONNECTING) { Log.d(TAG, "Bluetooth audio SCO is now connecting...") - } else if (audioState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { + } else if (audioState == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) { Log.d(TAG, "Bluetooth audio SCO is now disconnected") if (isInitialStateChange) { Log.d(TAG, "Ignore ${audioState.toStateString()} initial sticky broadcast.") @@ -298,10 +299,17 @@ class SignalBluetoothManager( } } } else if (intent.action == BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED) { - val connectionState: Int = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED) +// val connectionState: Int = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED) +// handler.post { +// if (state != State.UNINITIALIZED) { +// onAudioStateChanged(connectionState, isInitialStickyBroadcast) +// } +// } + } else if (intent.action == AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED) { + val scoState: Int = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.ERROR) handler.post { if (state != State.UNINITIALIZED) { - onAudioStateChanged(connectionState, isInitialStickyBroadcast) + onAudioStateChanged(scoState, isInitialStickyBroadcast) } } } @@ -346,3 +354,11 @@ private fun Int.toStateString(): String { else -> "UNKNOWN" } } + +private fun Int.toScoString(): String = when (this) { + AudioManager.SCO_AUDIO_STATE_DISCONNECTED -> "DISCONNECTED" + AudioManager.SCO_AUDIO_STATE_CONNECTED -> "CONNECTED" + AudioManager.SCO_AUDIO_STATE_CONNECTING -> "CONNECTING" + AudioManager.SCO_AUDIO_STATE_ERROR -> "ERROR" + else -> "UNKNOWN" +}