diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
index 20b48aecf..429789fe7 100644
--- a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
+++ b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt
@@ -35,11 +35,13 @@ import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.window.layout.FoldingFeature
import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.CallActiveFragmentBinding
import org.linphone.ui.call.CallActivity
+import org.linphone.ui.call.model.CallMediaEncryptionModel
import org.linphone.ui.call.model.ZrtpSasConfirmationDialogModel
import org.linphone.ui.call.viewmodel.CallsViewModel
import org.linphone.ui.call.viewmodel.CurrentCallViewModel
@@ -63,6 +65,8 @@ class ActiveCallFragment : GenericCallFragment() {
private var zrtpSasDialog: Dialog? = null
+ private var bottomSheetDialog: BottomSheetDialogFragment? = null
+
// For moving video preview purposes
private var previewX: Float = 0f
@@ -152,6 +156,10 @@ class ActiveCallFragment : GenericCallFragment() {
findNavController().navigate(action)
}
+ binding.setCallStatisticsClickListener {
+ showCallStatistics()
+ }
+
sharedViewModel = requireActivity().run {
ViewModelProvider(this)[SharedCallViewModel::class.java]
}
@@ -272,6 +280,12 @@ class ActiveCallFragment : GenericCallFragment() {
}
}
}
+
+ callViewModel.showMediaEncryptionStatisticsEvent.observe(viewLifecycleOwner) {
+ it.consume { model ->
+ showMediaEncryptionStatistics(model)
+ }
+ }
}
@SuppressLint("ClickableViewAccessibility")
@@ -295,6 +309,9 @@ class ActiveCallFragment : GenericCallFragment() {
zrtpSasDialog?.dismiss()
zrtpSasDialog = null
+ bottomSheetDialog?.dismiss()
+ bottomSheetDialog = null
+
binding.localPreviewVideoSurface.setOnTouchListener(null)
}
@@ -317,4 +334,17 @@ class ActiveCallFragment : GenericCallFragment() {
set.applyTo(constraintLayout)
}
+
+ private fun showCallStatistics() {
+ // TODO
+ }
+
+ private fun showMediaEncryptionStatistics(model: CallMediaEncryptionModel) {
+ val modalBottomSheet = MediaEncryptionStatisticsDialogFragment(model)
+ modalBottomSheet.show(
+ requireActivity().supportFragmentManager,
+ MediaEncryptionStatisticsDialogFragment.TAG
+ )
+ bottomSheetDialog = modalBottomSheet
+ }
}
diff --git a/app/src/main/java/org/linphone/ui/call/fragment/MediaEncryptionStatisticsDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/MediaEncryptionStatisticsDialogFragment.kt
new file mode 100644
index 000000000..110c799b8
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/call/fragment/MediaEncryptionStatisticsDialogFragment.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.call.fragment
+
+import android.app.Dialog
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.UiThread
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import org.linphone.databinding.CallMediaEncryptionStatsMenuBinding
+import org.linphone.ui.call.model.CallMediaEncryptionModel
+
+@UiThread
+class MediaEncryptionStatisticsDialogFragment(
+ private val model: CallMediaEncryptionModel,
+ private val onDismiss: (() -> Unit)? = null
+) : BottomSheetDialogFragment() {
+ companion object {
+ const val TAG = "MediaEncryptionStatisticsDialogFragment"
+ }
+
+ override fun onCancel(dialog: DialogInterface) {
+ onDismiss?.invoke()
+ super.onCancel(dialog)
+ }
+
+ override fun onDismiss(dialog: DialogInterface) {
+ onDismiss?.invoke()
+ super.onDismiss(dialog)
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
+ // Makes sure all menu entries are visible,
+ // required for landscape mode (otherwise only first item is visible)
+ dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
+ return dialog
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ val view = CallMediaEncryptionStatsMenuBinding.inflate(layoutInflater)
+
+ view.model = model
+
+ return view.root
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/call/model/CallMediaEncryptionModel.kt b/app/src/main/java/org/linphone/ui/call/model/CallMediaEncryptionModel.kt
new file mode 100644
index 000000000..81003a79f
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/call/model/CallMediaEncryptionModel.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.call.model
+
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.MutableLiveData
+import org.linphone.R
+import org.linphone.core.Call
+import org.linphone.core.MediaEncryption
+import org.linphone.core.StreamType
+import org.linphone.utils.AppUtils
+
+class CallMediaEncryptionModel @WorkerThread constructor(call: Call) {
+ val mediaEncryption = MutableLiveData()
+
+ val isMediaEncryptionZrtp = MutableLiveData()
+ val zrtpCipher = MutableLiveData()
+ val zrtpKeyAgreement = MutableLiveData()
+ val zrtpHash = MutableLiveData()
+ val zrtpAuthTag = MutableLiveData()
+ val zrtpAuthSas = MutableLiveData()
+
+ init {
+ isMediaEncryptionZrtp.postValue(false)
+
+ val stats = call.getStats(StreamType.Audio)
+ if (stats != null) {
+ // ZRTP stats are only available when authentication token isn't null !
+ if (call.currentParams.mediaEncryption == MediaEncryption.ZRTP && call.authenticationToken != null) {
+ isMediaEncryptionZrtp.postValue(true)
+
+ if (stats.isZrtpKeyAgreementAlgoPostQuantum) {
+ mediaEncryption.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_media_encryption,
+ AppUtils.getString(
+ R.string.call_stats_media_encryption_zrtp_post_quantum
+ )
+ )
+ )
+ } else {
+ mediaEncryption.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_media_encryption,
+ call.currentParams.mediaEncryption.name
+ )
+ )
+ }
+
+ zrtpCipher.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_zrtp_cipher_algo,
+ stats.zrtpCipherAlgo
+ )
+ )
+ zrtpKeyAgreement.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_zrtp_key_agreement_algo,
+ stats.zrtpKeyAgreementAlgo
+ )
+ )
+ zrtpHash.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_zrtp_hash_algo,
+ stats.zrtpHashAlgo
+ )
+ )
+ zrtpAuthTag.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_zrtp_auth_tag_algo,
+ stats.zrtpAuthTagAlgo
+ )
+ )
+ zrtpAuthSas.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_zrtp_sas_algo,
+ stats.zrtpSasAlgo
+ )
+ )
+ } else {
+ mediaEncryption.postValue(
+ AppUtils.getFormattedString(
+ R.string.call_stats_media_encryption,
+ call.currentParams.mediaEncryption.name
+ )
+ )
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt
index 9d2c6cd8a..72f361729 100644
--- a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt
@@ -41,6 +41,7 @@ import org.linphone.core.MediaDirection
import org.linphone.core.MediaEncryption
import org.linphone.core.tools.Log
import org.linphone.ui.call.model.AudioDeviceModel
+import org.linphone.ui.call.model.CallMediaEncryptionModel
import org.linphone.ui.call.model.ConferenceModel
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.ui.main.history.model.NumpadModel
@@ -135,6 +136,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
MutableLiveData>>()
}
+ val showMediaEncryptionStatisticsEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
+ }
+
// Conference
val conferenceModel = ConferenceModel()
@@ -652,6 +657,16 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
}
}
+ @UiThread
+ fun showMediaEncryptionStatisticsIfPossible(): Boolean {
+ coreContext.postOnCoreThread {
+ val model = CallMediaEncryptionModel(currentCall)
+ showMediaEncryptionStatisticsEvent.postValue(Event(model))
+ }
+
+ return true
+ }
+
@WorkerThread
private fun showZrtpSasDialog(authToken: String) {
val upperCaseAuthToken = authToken.uppercase(Locale.getDefault())
diff --git a/app/src/main/res/drawable/chart_bar.xml b/app/src/main/res/drawable/chart_bar.xml
new file mode 100644
index 000000000..6916f393e
--- /dev/null
+++ b/app/src/main/res/drawable/chart_bar.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-land/call_actions_bottom_sheet.xml b/app/src/main/res/layout-land/call_actions_bottom_sheet.xml
index cd38f9ef3..12e721cff 100644
--- a/app/src/main/res/layout-land/call_actions_bottom_sheet.xml
+++ b/app/src/main/res/layout-land/call_actions_bottom_sheet.xml
@@ -12,7 +12,7 @@
name="callsListClickListener"
type="View.OnClickListener" />
+
+
@@ -212,7 +227,7 @@
android:layout_height="wrap_content"
android:paddingBottom="15dp"
android:text="@string/call_action_show_dialer"
- app:layout_constraintEnd_toStartOf="@id/chat_label"
+ app:layout_constraintEnd_toStartOf="@id/stats_label"
app:layout_constraintStart_toEndOf="@id/calls_list_label"
app:layout_constraintTop_toBottomOf="@id/numpad" />
@@ -223,10 +238,23 @@
android:layout_height="wrap_content"
android:paddingBottom="15dp"
android:text="@string/call_action_show_messages"
+ android:visibility="gone"
app:layout_constraintEnd_toStartOf="@id/pause_call_label"
app:layout_constraintStart_toEndOf="@id/numpad_label"
app:layout_constraintTop_toBottomOf="@id/chat" />
+
+
+
@@ -123,12 +126,28 @@
android:padding="@dimen/call_button_icon_padding"
android:src="@drawable/chat_teardrop_text"
android:background="@drawable/shape_round_in_call_disabled_button_background"
+ android:visibility="gone"
app:tint="?attr/color_grey_500"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@id/transfer_label"
app:layout_constraintStart_toStartOf="@id/transfer"
app:layout_constraintEnd_toEndOf="@id/transfer"/>
+
+
+
+
+
@@ -195,6 +198,7 @@
+ bind:callsListClickListener="@{callsListClickListener}"
+ bind:callStatisticsClickListener="@{callStatisticsClickListener}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 302e858ac..83f76f09c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -498,6 +498,7 @@
Calls list
Dialer
Messages
+ Statistics
Pause
Resume
Record
@@ -524,6 +525,14 @@
Headset
Headphones
+ Media encryption: %s
+ Post Quantum ZRTP
+ Cipher algorithm: %s
+ Key agreement algorithm: %s
+ Hash algorithm: %s
+ Authentication algorithm: %s
+ SAS algorithm: %s
+
Share invitation
Waiting for other participants…
Screen share