Added early media advanced settings + added video views in incoming/outgoing call fragments to see/capture early media video if any

This commit is contained in:
Sylvain Berfini 2025-01-08 12:59:23 +01:00
parent 17e3622bdc
commit 00fbfcc490
17 changed files with 398 additions and 144 deletions

View file

@ -126,6 +126,20 @@ class CorePreferences
config.setBool("app", "show_confirmation_dialog_zrtp_trust_call", value)
}
@get:WorkerThread @set:WorkerThread
var acceptEarlyMedia: Boolean
get() = config.getBool("sip", "incoming_calls_early_media", false)
set(value) {
config.setBool("sip", "incoming_calls_early_media", value)
}
@get:WorkerThread @set:WorkerThread
var allowOutgoingEarlyMedia: Boolean
get() = config.getBool("misc", "real_early_media", false)
set(value) {
config.setBool("misc", "real_early_media", value)
}
// Conversation related
var markConversationAsReadWhenDismissingMessageNotification: Boolean

View file

@ -492,7 +492,7 @@ class CallActivity : GenericActivity() {
}
private fun hideUI(hide: Boolean) {
Log.i("$TAG Switching full screen mode to ${if (hide) "ON" else "OFF"}")
Log.i("$TAG Switching full screen mode to [${if (hide) "ON" else "OFF"}]")
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
if (hide) {
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())

View file

@ -19,14 +19,12 @@
*/
package org.linphone.ui.call.conference.fragment
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback
@ -78,33 +76,6 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { }
}
// For moving video preview purposes
private var previewX: Float = 0f
private var previewY: Float = 0f
private val previewTouchListener = View.OnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
previewX = view.x - event.rawX
previewY = view.y - event.rawY
true
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + previewX)
.y(event.rawY + previewY)
.setDuration(0)
.start()
true
}
else -> {
view.performClick()
false
}
}
}
private val backPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val actionsBottomSheetBehavior = BottomSheetBehavior.from(binding.bottomBar.root)
@ -329,23 +300,21 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
)
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
coreContext.postOnCoreThread {
binding.localPreviewVideoSurface.setOnTouchListener(previewTouchListener)
setupVideoPreview(binding.localPreviewVideoSurface)
coreContext.postOnCoreThread {
// Need to be done manually
callViewModel.updateCallDuration()
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onPause() {
super.onPause()
binding.localPreviewVideoSurface.setOnTouchListener(null)
cleanVideoPreview(binding.localPreviewVideoSurface)
}
private fun updateHingeRelatedConstraints(feature: FoldingFeature) {

View file

@ -19,12 +19,10 @@
*/
package org.linphone.ui.call.fragment
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
@ -67,33 +65,6 @@ class ActiveCallFragment : GenericCallFragment() {
private var zrtpSasDialog: Dialog? = null
// For moving video preview purposes
private var previewX: Float = 0f
private var previewY: Float = 0f
private val previewTouchListener = View.OnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
previewX = view.x - event.rawX
previewY = view.y - event.rawY
true
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + previewX)
.y(event.rawY + previewY)
.setDuration(0)
.start()
true
}
else -> {
view.performClick()
false
}
}
}
private val actionsBottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
@ -253,7 +224,7 @@ class ActiveCallFragment : GenericCallFragment() {
}
callViewModel.fullScreenMode.observe(viewLifecycleOwner) { hide ->
Log.i("$TAG Switching full screen mode to ${if (hide) "ON" else "OFF"}")
Log.i("$TAG Switching full screen mode to [${if (hide) "ON" else "OFF"}]")
sharedViewModel.toggleFullScreenEvent.value = Event(hide)
numpadBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
callStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
@ -420,14 +391,14 @@ class ActiveCallFragment : GenericCallFragment() {
)
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
coreContext.postOnCoreThread { core ->
core.nativeVideoWindowId = binding.remoteVideoSurface
setupVideoPreview(binding.localPreviewVideoSurface)
binding.localPreviewVideoSurface.setOnTouchListener(previewTouchListener)
coreContext.postOnCoreThread { core ->
Log.i("$TAG Fragment resuming, setting native video window ID")
core.nativeVideoWindowId = binding.remoteVideoSurface
// Need to be done manually
callViewModel.updateCallDuration()
@ -442,14 +413,13 @@ class ActiveCallFragment : GenericCallFragment() {
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onPause() {
super.onPause()
zrtpSasDialog?.dismiss()
zrtpSasDialog = null
binding.localPreviewVideoSurface.setOnTouchListener(null)
cleanVideoPreview(binding.localPreviewVideoSurface)
}
private fun updateHingeRelatedConstraints(feature: FoldingFeature) {

View file

@ -19,11 +19,14 @@
*/
package org.linphone.ui.call.fragment
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import org.linphone.ui.GenericFragment
import org.linphone.ui.call.view.RoundCornersTextureView
import org.linphone.ui.call.viewmodel.SharedCallViewModel
@UiThread
@ -34,6 +37,34 @@ abstract class GenericCallFragment : GenericFragment() {
protected lateinit var sharedViewModel: SharedCallViewModel
// For moving video preview purposes
private val videoPreviewTouchListener = View.OnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
sharedViewModel.videoPreviewX = view.x - event.rawX
sharedViewModel.videoPreviewY = view.y - event.rawY
true
}
MotionEvent.ACTION_UP -> {
sharedViewModel.videoPreviewX = view.translationX
sharedViewModel.videoPreviewY = view.translationY
true
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + sharedViewModel.videoPreviewX)
.y(event.rawY + sharedViewModel.videoPreviewY)
.setDuration(0)
.start()
true
}
else -> {
view.performClick()
false
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -41,4 +72,21 @@ abstract class GenericCallFragment : GenericFragment() {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
}
@SuppressLint("ClickableViewAccessibility")
protected fun setupVideoPreview(localPreviewVideoSurface: RoundCornersTextureView) {
// To restore video preview position if possible
localPreviewVideoSurface.animate()
.x(sharedViewModel.videoPreviewX)
.y(sharedViewModel.videoPreviewY)
.setDuration(0)
.start()
localPreviewVideoSurface.setOnTouchListener(videoPreviewTouchListener)
}
@SuppressLint("ClickableViewAccessibility")
protected fun cleanVideoPreview(localPreviewVideoSurface: RoundCornersTextureView) {
localPreviewVideoSurface.setOnTouchListener(null)
}
}

View file

@ -26,11 +26,16 @@ import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.tools.Log
import org.linphone.databinding.CallIncomingFragmentBinding
import org.linphone.ui.call.viewmodel.CurrentCallViewModel
@UiThread
class IncomingCallFragment : GenericCallFragment() {
companion object {
private const val TAG = "[Incoming Call Fragment]"
}
private lateinit var binding: CallIncomingFragmentBinding
private lateinit var callViewModel: CurrentCallViewModel
@ -53,6 +58,15 @@ class IncomingCallFragment : GenericCallFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = callViewModel
callViewModel.isIncomingEarlyMedia.observe(viewLifecycleOwner) { earlyMedia ->
if (earlyMedia) {
coreContext.postOnCoreThread { core ->
Log.i("$TAG Incoming early-media call, setting video surface")
core.nativeVideoWindowId = binding.remoteVideoSurface
}
}
}
}
override fun onResume() {

View file

@ -25,11 +25,17 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.tools.Log
import org.linphone.databinding.CallOutgoingFragmentBinding
import org.linphone.ui.call.viewmodel.CurrentCallViewModel
@UiThread
class OutgoingCallFragment : GenericCallFragment() {
companion object {
private const val TAG = "[Outgoing Call Fragment]"
}
private lateinit var binding: CallOutgoingFragmentBinding
private lateinit var callViewModel: CurrentCallViewModel
@ -52,5 +58,26 @@ class OutgoingCallFragment : GenericCallFragment() {
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = callViewModel
callViewModel.isOutgoingEarlyMedia.observe(viewLifecycleOwner) { earlyMedia ->
if (earlyMedia) {
coreContext.postOnCoreThread { core ->
Log.i("$TAG Outgoing early-media call with video, setting preview surface")
core.nativePreviewWindowId = binding.localPreviewVideoSurface
}
}
}
}
override fun onResume() {
super.onResume()
setupVideoPreview(binding.localPreviewVideoSurface)
}
override fun onPause() {
super.onPause()
cleanVideoPreview(binding.localPreviewVideoSurface)
}
}

View file

@ -87,10 +87,14 @@ class CurrentCallViewModel
val videoUpdateInProgress = MutableLiveData<Boolean>()
val isIncomingEarlyMedia = MutableLiveData<Boolean>()
val isOutgoing = MutableLiveData<Boolean>()
val isOutgoingRinging = MutableLiveData<Boolean>()
val isOutgoingEarlyMedia = MutableLiveData<Boolean>()
val isRecordingEnabled = MutableLiveData<Boolean>()
val isRecording = MutableLiveData<Boolean>()
@ -420,6 +424,8 @@ class CurrentCallViewModel
message: String
) {
isOutgoingRinging.postValue(call.state == Call.State.OutgoingRinging)
isIncomingEarlyMedia.postValue(call.state == Call.State.IncomingEarlyMedia)
isOutgoingEarlyMedia.postValue(call.state == Call.State.OutgoingEarlyMedia)
if (::currentCall.isInitialized) {
if (call != currentCall) {
@ -1129,10 +1135,13 @@ class CurrentCallViewModel
updateOutputAudioDevice(audioDevice)
isOutgoing.postValue(call.dir == Call.Dir.Outgoing)
isOutgoingRinging.postValue(call.state == Call.State.OutgoingRinging)
val state = call.state
isOutgoingRinging.postValue(state == Call.State.OutgoingRinging)
isIncomingEarlyMedia.postValue(state == Call.State.IncomingEarlyMedia)
isOutgoingEarlyMedia.postValue(state == Call.State.OutgoingEarlyMedia)
isPaused.postValue(isCallPaused())
isPausedByRemote.postValue(call.state == Call.State.PausedByRemote)
isPausedByRemote.postValue(state == Call.State.PausedByRemote)
canBePaused.postValue(canCallBePaused())
val address = call.callLog.remoteAddress

View file

@ -31,4 +31,8 @@ class SharedCallViewModel
val toggleFullScreenEvent = MutableLiveData<Event<Boolean>>()
val foldingState = MutableLiveData<FoldingFeature>()
// For moving video preview purposes
var videoPreviewX: Float = 0f
var videoPreviewY: Float = 0f
}

View file

@ -187,6 +187,8 @@ class SettingsViewModel
val mediaEncryptionLabels = arrayListOf<String>()
private val mediaEncryptionValues = arrayListOf<MediaEncryption>()
val mediaEncryptionMandatory = MutableLiveData<Boolean>()
val acceptEarlyMedia = MutableLiveData<Boolean>()
val allowOutgoingEarlyMedia = MutableLiveData<Boolean>()
val expandAudioDevices = MutableLiveData<Boolean>()
val inputAudioDeviceIndex = MutableLiveData<Int>()
@ -671,6 +673,8 @@ class SettingsViewModel
}
mediaEncryptionMandatory.postValue(core.isMediaEncryptionMandatory)
acceptEarlyMedia.postValue(corePreferences.acceptEarlyMedia)
allowOutgoingEarlyMedia.postValue(corePreferences.allowOutgoingEarlyMedia)
}
@UiThread
@ -696,6 +700,26 @@ class SettingsViewModel
}
}
@UiThread
fun toggleAcceptEarlyMedia() {
val newValue = acceptEarlyMedia.value == false
coreContext.postOnCoreThread { core ->
corePreferences.acceptEarlyMedia = newValue
acceptEarlyMedia.postValue(newValue)
}
}
@UiThread
fun toggleAllowOutgoingEarlyMedia() {
val newValue = allowOutgoingEarlyMedia.value == false
coreContext.postOnCoreThread { core ->
corePreferences.allowOutgoingEarlyMedia = newValue
allowOutgoingEarlyMedia.postValue(newValue)
}
}
@UiThread
fun updateDeviceName() {
coreContext.postOnCoreThread {

View file

@ -15,44 +15,6 @@
android:layout_height="match_parent"
android:background="@color/gray_900">
<ImageView
android:id="@+id/back"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="invisible"
app:tint="@color/bc_white" />
<ImageView
android:id="@+id/call_direction_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="@drawable/incoming_call"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/call_header_style"
android:id="@+id/call_direction_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="@{viewModel.incomingCallTitle, default=@string/call_video_incoming}"
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
app:layout_constraintTop_toTopOf="@id/back"
app:layout_constraintBottom_toBottomOf="@id/back"/>
<include
android:id="@+id/avatar"
android:layout_width="@dimen/avatar_in_call_size"
@ -113,6 +75,39 @@
app:layout_constraintTop_toTopOf="@id/name_address"
app:layout_constraintBottom_toBottomOf="@id/name_address" />
<org.linphone.ui.call.view.RoundCornersTextureView
android:id="@+id/remote_video_surface"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/call_direction_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="@drawable/incoming_call"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/call_header_style"
android:id="@+id/call_direction_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="@{viewModel.incomingCallTitle, default=@string/call_video_incoming}"
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
app:layout_constraintTop_toTopOf="parent"/>
<include
bind:viewModel="@{viewModel}"
android:id="@+id/bottom_bar"
@ -123,6 +118,33 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/early_media_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@{viewModel.displayedName, default=`John Doe`}"
android:textColor="@color/bc_white"
android:textSize="22sp"
android:visibility="@{viewModel.isIncomingEarlyMedia ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toTopOf="@id/early_media_address"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/early_media_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginBottom="5dp"
android:text="@{viewModel.displayedAddress, default=`sip:johndoe@sip.linphone.org`}"
android:textColor="@color/bc_white"
android:textSize="14sp"
android:visibility="@{viewModel.isIncomingEarlyMedia ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -98,6 +98,21 @@
</LinearLayout>
<ImageView
android:onClick="@{() -> viewModel.switchCamera()}"
android:id="@+id/switch_camera"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="10dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:src="@drawable/camera_rotate"
android:contentDescription="@string/content_description_change_camera"
android:visibility="gone"
app:tint="@color/bc_white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/in_progress"
android:layout_width="wrap_content"
@ -105,11 +120,25 @@
android:indeterminate="true"
app:indicatorColor="@color/bc_white"
app:indicatorSize="28dp"
app:layout_constraintStart_toEndOf="@id/name_address"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/avatar"
app:layout_constraintTop_toTopOf="@id/name_address"
app:layout_constraintBottom_toBottomOf="@id/name_address" />
<org.linphone.ui.call.view.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
app:alignTopRight="true"
app:displayMode="black_bars"
roundCornersRadius="@dimen/call_round_corners_texture_view_radius"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/call_video_preview_max_size"
app:layout_constraintWidth_max="@dimen/call_video_preview_max_size" />
<include
bind:viewModel="@{viewModel}"
android:id="@+id/bottom_bar"

View file

@ -15,30 +15,6 @@
android:layout_height="match_parent"
android:background="@color/gray_900">
<ImageView
android:id="@+id/call_direction_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="@drawable/incoming_call"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/call_header_style"
android:id="@+id/call_direction_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="@{viewModel.incomingCallTitle, default=@string/call_video_incoming}"
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/in_progress"
android:layout_width="wrap_content"
@ -92,6 +68,66 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<org.linphone.ui.call.view.RoundCornersTextureView
android:id="@+id/remote_video_surface"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/call_direction_icon"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="@drawable/incoming_call"
android:contentDescription="@null"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/call_direction_label"
app:layout_constraintBottom_toBottomOf="@id/call_direction_label"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/call_header_style"
android:id="@+id/call_direction_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:text="@{viewModel.incomingCallTitle, default=@string/call_video_incoming}"
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/early_media_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="@{viewModel.displayedName, default=`John Doe`}"
android:textColor="@color/bc_white"
android:textSize="22sp"
android:visibility="@{viewModel.isIncomingEarlyMedia ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toTopOf="@id/early_media_address"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style"
android:id="@+id/early_media_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginBottom="5dp"
android:text="@{viewModel.displayedAddress, default=`sip:johndoe@sip.linphone.org`}"
android:textColor="@color/bc_white"
android:textSize="14sp"
android:visibility="@{viewModel.isIncomingEarlyMedia ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintStart_toStartOf="parent" />
<include
bind:viewModel="@{viewModel}"
android:id="@+id/bottom_bar"

View file

@ -39,6 +39,21 @@
app:layout_constraintStart_toEndOf="@id/call_direction_icon"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:onClick="@{() -> viewModel.switchCamera()}"
android:id="@+id/switch_camera"
android:layout_width="wrap_content"
android:layout_height="@dimen/top_bar_height"
android:padding="10dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:src="@drawable/camera_rotate"
android:contentDescription="@string/content_description_change_camera"
android:visibility="gone"
app:tint="@color/bc_white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/in_progress"
android:layout_width="wrap_content"
@ -91,6 +106,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<org.linphone.ui.call.view.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
app:alignTopRight="true"
app:displayMode="black_bars"
roundCornersRadius="@dimen/call_round_corners_texture_view_radius"
app:layout_constraintBottom_toTopOf="@id/bottom_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="@dimen/call_video_preview_max_size"
app:layout_constraintWidth_max="@dimen/call_video_preview_max_size" />
<include
bind:viewModel="@{viewModel}"
android:id="@+id/bottom_bar"

View file

@ -57,6 +57,21 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatTextView
style="@style/settings_title_style"
android:id="@+id/keep_alive_service_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="10dp"
android:text="@string/settings_advanced_keep_alive_service_title"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="@id/keep_alive_service_switch"
app:layout_constraintBottom_toBottomOf="@id/keep_alive_service_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/keep_alive_service_switch"/>
<com.google.android.material.materialswitch.MaterialSwitch
style="@style/material_switch_style"
android:id="@+id/keep_alive_service_switch"
@ -174,21 +189,61 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/media_encryption" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/settings_title_style"
android:id="@+id/push_notifications_title"
android:onClick="@{() -> viewModel.toggleAcceptEarlyMedia()}"
android:id="@+id/accept_early_media_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="10dp"
android:text="@string/settings_advanced_keep_alive_service_title"
android:text="@string/settings_advanced_accept_early_media_title"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="@id/keep_alive_service_switch"
app:layout_constraintBottom_toBottomOf="@id/keep_alive_service_switch"
app:layout_constraintTop_toTopOf="@id/accept_early_media_switch"
app:layout_constraintBottom_toBottomOf="@id/accept_early_media_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/keep_alive_service_switch"/>
app:layout_constraintEnd_toStartOf="@id/accept_early_media_switch"/>
<com.google.android.material.materialswitch.MaterialSwitch
style="@style/material_switch_style"
android:id="@+id/accept_early_media_switch"
android:onClick="@{() -> viewModel.toggleAcceptEarlyMedia()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:checked="@{viewModel.acceptEarlyMedia}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/media_encryption_mandatory_switch" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/settings_title_style"
android:onClick="@{() -> viewModel.toggleAllowOutgoingEarlyMedia()}"
android:id="@+id/allow_outgoing_early_media_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="10dp"
android:text="@string/settings_advanced_allow_outgoing_early_media_title"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintTop_toTopOf="@id/allow_outgoing_early_media_switch"
app:layout_constraintBottom_toBottomOf="@id/allow_outgoing_early_media_switch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/allow_outgoing_early_media_switch"/>
<com.google.android.material.materialswitch.MaterialSwitch
style="@style/material_switch_style"
android:id="@+id/allow_outgoing_early_media_switch"
android:onClick="@{() -> viewModel.toggleAllowOutgoingEarlyMedia()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginEnd="16dp"
android:checked="@{viewModel.allowOutgoingEarlyMedia}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/accept_early_media_switch" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/settings_title_style"
@ -201,7 +256,7 @@
android:paddingBottom="8dp"
android:text="@string/settings_advanced_device_id"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/media_encryption_mandatory_switch"/>
app:layout_constraintTop_toBottomOf="@id/allow_outgoing_early_media_switch"/>
<androidx.appcompat.widget.AppCompatEditText
style="@style/default_text_style"

View file

@ -257,6 +257,8 @@
<string name="settings_advanced_upload_server_url">URL du serveur de partage de fichier</string>
<string name="settings_advanced_media_encryption_title">Chiffrement du média</string>
<string name="settings_advanced_media_encryption_mandatory_title">Rendre le chiffrement du média obligatoire</string>
<string name="settings_advanced_accept_early_media_title">Accepter l\'early media</string>
<string name="settings_advanced_allow_outgoing_early_media_title">Autoriser l\'early media pour les appels sortants</string>
<string name="settings_advanced_remote_provisioning_url">URL de configuration distante</string>
<string name="settings_advanced_download_apply_remote_provisioning">Télécharger &amp; appliquer</string>
<string name="settings_advanced_audio_devices_title">Périphériques audio</string>

View file

@ -296,6 +296,8 @@
<string name="settings_advanced_upload_server_url">File sharing server URL</string>
<string name="settings_advanced_media_encryption_title">Media encryption</string>
<string name="settings_advanced_media_encryption_mandatory_title">Media encryption mandatory</string>
<string name="settings_advanced_accept_early_media_title">Accept early media</string>
<string name="settings_advanced_allow_outgoing_early_media_title">Allow outgoing early media</string>
<string name="settings_advanced_remote_provisioning_url">Remote provisioning URL</string>
<string name="settings_advanced_download_apply_remote_provisioning">Download &amp; apply</string>
<string name="settings_advanced_audio_devices_title">Audio devices</string>