mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 05:37:47 +00:00
feat: adding a call time display
This commit is contained in:
@@ -18,7 +18,9 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
import kotlinx.android.synthetic.main.activity_conversation_v2.*
|
||||||
import kotlinx.android.synthetic.main.activity_webrtc.*
|
import kotlinx.android.synthetic.main.activity_webrtc.*
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.R
|
import network.loki.messenger.R
|
||||||
import org.session.libsession.avatars.ProfileContactPhoto
|
import org.session.libsession.avatars.ProfileContactPhoto
|
||||||
@@ -33,6 +35,7 @@ import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
|
|||||||
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
||||||
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.*
|
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.*
|
||||||
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.*
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.*
|
||||||
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -44,6 +47,8 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
const val ACTION_END = "end-call"
|
const val ACTION_END = "end-call"
|
||||||
|
|
||||||
const val BUSY_SIGNAL_DELAY_FINISH = 5500L
|
const val BUSY_SIGNAL_DELAY_FINISH = 5500L
|
||||||
|
|
||||||
|
val CALL_DURATION_FORMAT =
|
||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel by viewModels<CallViewModel>()
|
private val viewModel by viewModels<CallViewModel>()
|
||||||
@@ -186,6 +191,20 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launch {
|
||||||
|
while (isActive) {
|
||||||
|
val startTime = viewModel.callStartTime
|
||||||
|
if (startTime == -1L) {
|
||||||
|
callTime.isVisible = false
|
||||||
|
} else {
|
||||||
|
callTime.isVisible = true
|
||||||
|
callTime.text = Duration.Duration.ofMillis(startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(5_000) // update the call duration less frequently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
viewModel.localAudioEnabledState.collect { isEnabled ->
|
viewModel.localAudioEnabledState.collect { isEnabled ->
|
||||||
// change drawable background to enabled or not
|
// change drawable background to enabled or not
|
||||||
|
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.webrtc.locks.LockManager
|
|||||||
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
|
import org.thoughtcrime.securesms.webrtc.video.CameraEventListener
|
||||||
import org.thoughtcrime.securesms.webrtc.video.CameraState
|
import org.thoughtcrime.securesms.webrtc.video.CameraState
|
||||||
import org.webrtc.*
|
import org.webrtc.*
|
||||||
|
import org.webrtc.PeerConnection.IceConnectionState
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@@ -104,8 +105,6 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
val currentConnectionState
|
val currentConnectionState
|
||||||
get() = (_connectionEvents.value as CallStateUpdate).state
|
get() = (_connectionEvents.value as CallStateUpdate).state
|
||||||
|
|
||||||
private val networkExecutor = Executors.newSingleThreadExecutor()
|
|
||||||
|
|
||||||
private var eglBase: EglBase? = null
|
private var eglBase: EglBase? = null
|
||||||
|
|
||||||
var pendingOffer: String? = null
|
var pendingOffer: String? = null
|
||||||
@@ -117,6 +116,7 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
field = value
|
field = value
|
||||||
_recipientEvents.value = RecipientUpdate(value)
|
_recipientEvents.value = RecipientUpdate(value)
|
||||||
}
|
}
|
||||||
|
var callStartTime: Long = -1
|
||||||
var isReestablishing: Boolean = false
|
var isReestablishing: Boolean = false
|
||||||
|
|
||||||
private var peerConnection: PeerConnectionWrapper? = null
|
private var peerConnection: PeerConnectionWrapper? = null
|
||||||
@@ -209,8 +209,11 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
peerConnectionObservers.forEach { listener -> listener.onSignalingChange(newState) }
|
peerConnectionObservers.forEach { listener -> listener.onSignalingChange(newState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
override fun onIceConnectionChange(newState: IceConnectionState) {
|
||||||
peerConnectionObservers.forEach { listener -> listener.onIceConnectionChange(newState) }
|
peerConnectionObservers.forEach { listener -> listener.onIceConnectionChange(newState) }
|
||||||
|
if (newState == IceConnectionState.CONNECTED) {
|
||||||
|
callStartTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
||||||
@@ -340,6 +343,9 @@ class CallManager(context: Context, audioManager: AudioManagerCompat, private va
|
|||||||
localCameraState = CameraState.UNKNOWN
|
localCameraState = CameraState.UNKNOWN
|
||||||
recipient = null
|
recipient = null
|
||||||
callId = null
|
callId = null
|
||||||
|
pendingOfferTime = -1
|
||||||
|
pendingOffer = null
|
||||||
|
callStartTime = -1
|
||||||
_audioEvents.value = AudioEnabled(false)
|
_audioEvents.value = AudioEnabled(false)
|
||||||
_videoEvents.value = VideoEnabled(false)
|
_videoEvents.value = VideoEnabled(false)
|
||||||
_remoteVideoEvents.value = VideoEnabled(false)
|
_remoteVideoEvents.value = VideoEnabled(false)
|
||||||
|
@@ -11,6 +11,23 @@ import javax.inject.Inject
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class CallViewModel @Inject constructor(private val callManager: CallManager): ViewModel() {
|
class CallViewModel @Inject constructor(private val callManager: CallManager): ViewModel() {
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
CALL_PENDING,
|
||||||
|
|
||||||
|
CALL_PRE_INIT,
|
||||||
|
CALL_INCOMING,
|
||||||
|
CALL_OUTGOING,
|
||||||
|
CALL_CONNECTED,
|
||||||
|
CALL_RINGING,
|
||||||
|
CALL_BUSY,
|
||||||
|
CALL_DISCONNECTED,
|
||||||
|
|
||||||
|
NETWORK_FAILURE,
|
||||||
|
RECIPIENT_UNAVAILABLE,
|
||||||
|
NO_SUCH_USER,
|
||||||
|
UNTRUSTED_IDENTITY,
|
||||||
|
}
|
||||||
|
|
||||||
val localRenderer: SurfaceViewRenderer?
|
val localRenderer: SurfaceViewRenderer?
|
||||||
get() = callManager.localRenderer
|
get() = callManager.localRenderer
|
||||||
|
|
||||||
@@ -31,24 +48,6 @@ class CallViewModel @Inject constructor(private val callManager: CallManager): V
|
|||||||
val isSpeaker: Boolean
|
val isSpeaker: Boolean
|
||||||
get() = _isSpeaker
|
get() = _isSpeaker
|
||||||
|
|
||||||
|
|
||||||
enum class State {
|
|
||||||
CALL_PENDING,
|
|
||||||
|
|
||||||
CALL_PRE_INIT,
|
|
||||||
CALL_INCOMING,
|
|
||||||
CALL_OUTGOING,
|
|
||||||
CALL_CONNECTED,
|
|
||||||
CALL_RINGING,
|
|
||||||
CALL_BUSY,
|
|
||||||
CALL_DISCONNECTED,
|
|
||||||
|
|
||||||
NETWORK_FAILURE,
|
|
||||||
RECIPIENT_UNAVAILABLE,
|
|
||||||
NO_SUCH_USER,
|
|
||||||
UNTRUSTED_IDENTITY,
|
|
||||||
}
|
|
||||||
|
|
||||||
val audioDeviceState
|
val audioDeviceState
|
||||||
get() = callManager.audioDeviceEvents
|
get() = callManager.audioDeviceEvents
|
||||||
.onEach {
|
.onEach {
|
||||||
@@ -73,4 +72,7 @@ class CallViewModel @Inject constructor(private val callManager: CallManager): V
|
|||||||
val recipient
|
val recipient
|
||||||
get() = callManager.recipientEvents
|
get() = callManager.recipientEvents
|
||||||
|
|
||||||
|
val callStartTime: Long
|
||||||
|
get() = callManager.callStartTime
|
||||||
|
|
||||||
}
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:theme="@style/Theme.Session.CallActivity"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:keepScreenOn="true"
|
android:keepScreenOn="true"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -41,6 +42,17 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@id/remote_recipient"
|
app:layout_constraintTop_toBottomOf="@id/remote_recipient"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:textSize="@dimen/text_size"
|
||||||
|
android:textColor="@color/text"
|
||||||
|
tools:text="00:05:20"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/controlGroupBarrier"
|
||||||
|
android:layout_marginBottom="@dimen/medium_spacing"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
@@ -183,4 +195,13 @@
|
|||||||
android:id="@+id/incomingControlGroup"
|
android:id="@+id/incomingControlGroup"
|
||||||
app:constraint_referenced_ids="acceptCallButton,declineCallButton"/>
|
app:constraint_referenced_ids="acceptCallButton,declineCallButton"/>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/controlGroupBarrier"
|
||||||
|
app:barrierDirection="top"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:constraint_referenced_ids="switchCameraButton,enableCameraButton,microphoneButton,speakerPhoneButton"
|
||||||
|
/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@@ -137,8 +137,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Session.CallActivity" parent="Theme.Session.ForceDark">
|
<style name="Theme.Session.CallActivity" parent="Theme.Session.ForceDark">
|
||||||
<item name="windowActionBar">false</item>
|
<!-- in case we want to add customisation like no title -->
|
||||||
<item name="windowNoTitle">true</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Session.BottomSheet" parent="@style/Theme.AppCompat.DayNight.Dialog">
|
<style name="Theme.Session.BottomSheet" parent="@style/Theme.AppCompat.DayNight.Dialog">
|
||||||
|
Reference in New Issue
Block a user