From 308aff5392f05ba1b650669698e03d2f91143706 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 26 Feb 2024 10:37:12 +0100 Subject: [PATCH] Started call stats --- .../ui/call/fragment/ActiveCallFragment.kt | 25 ++- .../AudioDevicesMenuDialogFragment.kt | 5 +- .../call/fragment/CallMenuDialogFragment.kt | 1 + .../fragment/CallStatisticsDialogFragment.kt | 74 ++++++++ .../ConferenceLayoutMenuDialogFragment.kt | 5 +- ...MediaEncryptionStatisticsDialogFragment.kt | 5 +- .../linphone/ui/call/model/CallStatsModel.kt | 100 ++++++++++ .../model/ConferenceParticipantDeviceModel.kt | 26 ++- .../ui/call/viewmodel/CurrentCallViewModel.kt | 9 + .../main/res/color/in_call_button_color.xml | 7 + ...pe_squircle_in_call_button_background.xml} | 3 +- ...l_media_encryption_stats_bottom_sheet.xml} | 0 .../layout-land/call_stats_bottom_sheet.xml | 173 ++++++++++++++++++ .../call_active_conference_fragment.xml | 2 +- .../main/res/layout/call_active_fragment.xml | 8 + ...ml => call_audio_devices_bottom_sheet.xml} | 0 ...> call_conference_layout_bottom_sheet.xml} | 0 ...l_media_encryption_stats_bottom_sheet.xml} | 0 .../res/layout/call_stats_bottom_sheet.xml | 169 +++++++++++++++++ app/src/main/res/values/strings.xml | 7 + 20 files changed, 596 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/org/linphone/ui/call/fragment/CallStatisticsDialogFragment.kt create mode 100644 app/src/main/java/org/linphone/ui/call/model/CallStatsModel.kt create mode 100644 app/src/main/res/color/in_call_button_color.xml rename app/src/main/res/drawable/{shape_squircle_main2_400_border.xml => shape_squircle_in_call_button_background.xml} (57%) rename app/src/main/res/layout-land/{call_media_encryption_stats_menu.xml => call_media_encryption_stats_bottom_sheet.xml} (100%) create mode 100644 app/src/main/res/layout-land/call_stats_bottom_sheet.xml rename app/src/main/res/layout/{call_audio_devices_menu.xml => call_audio_devices_bottom_sheet.xml} (100%) rename app/src/main/res/layout/{call_conference_layout_menu.xml => call_conference_layout_bottom_sheet.xml} (100%) rename app/src/main/res/layout/{call_media_encryption_stats_menu.xml => call_media_encryption_stats_bottom_sheet.xml} (100%) create mode 100644 app/src/main/res/layout/call_stats_bottom_sheet.xml 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 eb64de6ec..70a35499f 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 @@ -107,6 +107,17 @@ class ActiveCallFragment : GenericCallFragment() { override fun onSlide(bottomSheet: View, slideOffset: Float) { } } + private val callStatsBottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_COLLAPSED) { + val callStatsBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet) + callStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) { } + } + private val actionsBottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() { override fun onStateChanged(bottomSheet: View, newState: Int) { if (newState == BottomSheetBehavior.STATE_EXPANDED) { @@ -159,6 +170,7 @@ class ActiveCallFragment : GenericCallFragment() { binding.viewModel = callViewModel binding.callsViewModel = callsViewModel binding.numpadModel = callViewModel.numpadModel + binding.callStatsModel = callViewModel.callStatsModel val actionsBottomSheetBehavior = BottomSheetBehavior.from(binding.bottomBar.root) actionsBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED @@ -168,6 +180,10 @@ class ActiveCallFragment : GenericCallFragment() { numpadBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN numpadBottomSheetBehavior.addBottomSheetCallback(numpadBottomSheetCallback) + val callStatsBottomSheetBehavior = BottomSheetBehavior.from(binding.callStats.root) + callStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN + callStatsBottomSheetBehavior.addBottomSheetCallback(callStatsBottomSheetCallback) + binding.setBackClickListener { requireActivity().finish() } @@ -183,7 +199,9 @@ class ActiveCallFragment : GenericCallFragment() { } binding.setCallStatisticsClickListener { - showCallStatistics() + actionsBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + numpadBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + callStatsBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED } sharedViewModel = requireActivity().run { @@ -205,6 +223,7 @@ class ActiveCallFragment : GenericCallFragment() { 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 } callViewModel.showZrtpSasDialogEvent.observe(viewLifecycleOwner) { @@ -371,10 +390,6 @@ class ActiveCallFragment : GenericCallFragment() { set.applyTo(constraintLayout) } - private fun showCallStatistics() { - // TODO - } - private fun showMediaEncryptionStatistics(model: CallMediaEncryptionModel) { val modalBottomSheet = MediaEncryptionStatisticsDialogFragment(model) modalBottomSheet.show( diff --git a/app/src/main/java/org/linphone/ui/call/fragment/AudioDevicesMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/AudioDevicesMenuDialogFragment.kt index 73ccc25ff..7816fe9d9 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/AudioDevicesMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/AudioDevicesMenuDialogFragment.kt @@ -30,7 +30,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.linphone.R -import org.linphone.databinding.CallAudioDevicesMenuBinding +import org.linphone.databinding.CallAudioDevicesBottomSheetBinding import org.linphone.ui.call.model.AudioDeviceModel @UiThread @@ -68,7 +68,8 @@ class AudioDevicesMenuDialogFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - val view = CallAudioDevicesMenuBinding.inflate(layoutInflater) + val view = CallAudioDevicesBottomSheetBinding.inflate(layoutInflater) + view.lifecycleOwner = viewLifecycleOwner for (device in devicesList) { device.dismissDialog = { diff --git a/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt index 99ec08a8f..18741dc35 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt @@ -63,6 +63,7 @@ class CallMenuDialogFragment( savedInstanceState: Bundle? ): View { val view = CallsListLongPressMenuBinding.inflate(layoutInflater) + view.lifecycleOwner = viewLifecycleOwner view.setHangUpClickListener { callModel.hangUp() diff --git a/app/src/main/java/org/linphone/ui/call/fragment/CallStatisticsDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/CallStatisticsDialogFragment.kt new file mode 100644 index 000000000..d903bc309 --- /dev/null +++ b/app/src/main/java/org/linphone/ui/call/fragment/CallStatisticsDialogFragment.kt @@ -0,0 +1,74 @@ +/* + * 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.CallStatsBottomSheetBindingImpl +import org.linphone.ui.call.model.CallStatsModel + +@UiThread +class CallStatisticsDialogFragment( + private val model: CallStatsModel, + private val onDismiss: (() -> Unit)? = null +) : BottomSheetDialogFragment() { + companion object { + const val TAG = "CallStatisticsDialogFragment" + } + + 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 = CallStatsBottomSheetBindingImpl.inflate(layoutInflater) + view.lifecycleOwner = viewLifecycleOwner + + view.model = model + + return view.root + } +} diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ConferenceLayoutMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ConferenceLayoutMenuDialogFragment.kt index 8fd179ac4..7ad9736fb 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/ConferenceLayoutMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/ConferenceLayoutMenuDialogFragment.kt @@ -29,7 +29,7 @@ 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.CallConferenceLayoutMenuBinding +import org.linphone.databinding.CallConferenceLayoutBottomSheetBinding import org.linphone.ui.call.model.ConferenceModel @UiThread @@ -64,7 +64,8 @@ class ConferenceLayoutMenuDialogFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - val view = CallConferenceLayoutMenuBinding.inflate(layoutInflater) + val view = CallConferenceLayoutBottomSheetBinding.inflate(layoutInflater) + view.lifecycleOwner = viewLifecycleOwner view.viewModel = conferenceModel 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 index 110c799b8..ce758eada 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/MediaEncryptionStatisticsDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/MediaEncryptionStatisticsDialogFragment.kt @@ -29,7 +29,7 @@ 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.databinding.CallMediaEncryptionStatsBottomSheetBinding import org.linphone.ui.call.model.CallMediaEncryptionModel @UiThread @@ -64,7 +64,8 @@ class MediaEncryptionStatisticsDialogFragment( container: ViewGroup?, savedInstanceState: Bundle? ): View { - val view = CallMediaEncryptionStatsMenuBinding.inflate(layoutInflater) + val view = CallMediaEncryptionStatsBottomSheetBinding.inflate(layoutInflater) + view.lifecycleOwner = viewLifecycleOwner view.model = model diff --git a/app/src/main/java/org/linphone/ui/call/model/CallStatsModel.kt b/app/src/main/java/org/linphone/ui/call/model/CallStatsModel.kt new file mode 100644 index 000000000..efd5f9f4b --- /dev/null +++ b/app/src/main/java/org/linphone/ui/call/model/CallStatsModel.kt @@ -0,0 +1,100 @@ +/* + * 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 kotlin.math.roundToInt +import org.linphone.R +import org.linphone.core.Call +import org.linphone.core.CallStats +import org.linphone.core.StreamType +import org.linphone.utils.AppUtils + +class CallStatsModel @WorkerThread constructor() { + val audioCodec = MutableLiveData() + val audioBandwidth = MutableLiveData() + + val isVideoEnabled = MutableLiveData() + val videoCodec = MutableLiveData() + val videoBandwidth = MutableLiveData() + val videoResolution = MutableLiveData() + val videoFps = MutableLiveData() + + @WorkerThread + fun update(call: Call, stats: CallStats?) { + stats ?: return + isVideoEnabled.postValue(call.params.isVideoEnabled) + + when (stats.type) { + StreamType.Audio -> { + val payloadType = call.currentParams.usedAudioPayloadType + val clockRate = (payloadType?.clockRate ?: 0) / 1000 + val codecLabel = AppUtils.getFormattedString( + R.string.call_stats_codec_label, + "${payloadType?.mimeType}/$clockRate kHz" + ) + audioCodec.postValue(codecLabel) + + val uploadBandwidth = stats.uploadBandwidth.roundToInt() + val downloadBandwidth = stats.downloadBandwidth.roundToInt() + val bandwidthLabel = AppUtils.getFormattedString( + R.string.call_stats_bandwidth_label, + "↑ $uploadBandwidth kbits/s ↓ $downloadBandwidth kbits/s" + ) + audioBandwidth.postValue(bandwidthLabel) + } + StreamType.Video -> { + val payloadType = call.currentParams.usedVideoPayloadType + val clockRate = (payloadType?.clockRate ?: 0) / 1000 + val codecLabel = AppUtils.getFormattedString( + R.string.call_stats_codec_label, + "${payloadType?.mimeType}/$clockRate kHz" + ) + videoCodec.postValue(codecLabel) + + val uploadBandwidth = stats.uploadBandwidth.roundToInt() + val downloadBandwidth = stats.downloadBandwidth.roundToInt() + val bandwidthLabel = AppUtils.getFormattedString( + R.string.call_stats_bandwidth_label, + "↑ $uploadBandwidth kbits/s ↓ $downloadBandwidth kbits/s" + ) + videoBandwidth.postValue(bandwidthLabel) + + val sentResolution = call.currentParams.sentVideoDefinition?.name + val receivedResolution = call.currentParams.receivedVideoDefinition?.name + val resolutionLabel = AppUtils.getFormattedString( + R.string.call_stats_resolution_label, + "↑ $sentResolution ↓ $receivedResolution" + ) + videoResolution.postValue(resolutionLabel) + + val sentFps = call.currentParams.sentFramerate.roundToInt() + val receivedFps = call.currentParams.receivedFramerate.roundToInt() + val fpsLabel = AppUtils.getFormattedString( + R.string.call_stats_fps_label, + "↑ $sentFps ↓ $receivedFps" + ) + videoFps.postValue(fpsLabel) + } + else -> {} + } + } +} diff --git a/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt b/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt index e79c91e08..1a6600838 100644 --- a/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt +++ b/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt @@ -116,7 +116,7 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( if (streamType == StreamType.Video) { isVideoAvailable.postValue(available) if (available) { - updateWindowId(textureView) + updateWindowId() } } } @@ -182,20 +182,26 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( ) textureView = view coreContext.postOnCoreThread { - updateWindowId(textureView) + updateWindowId() } } @WorkerThread - private fun updateWindowId(windowId: Any?) { - Log.i( - "$$TAG Setting participant [${device.address.asStringUriOnly()}] window ID [$windowId]" - ) - // SDK does it but it's a bit better this way, prevents going to participants map in PlatformHelper for nothing - if (isMe) { - coreContext.core.nativePreviewWindowId = windowId + private fun updateWindowId() { + if (::textureView.isInitialized) { + Log.i( + "$$TAG Setting participant [${device.address.asStringUriOnly()}] window ID [$textureView]" + ) + // SDK does it but it's a bit better this way, prevents going to participants map in PlatformHelper for nothing + if (isMe) { + coreContext.core.nativePreviewWindowId = textureView + } else { + device.nativeVideoWindowId = textureView + } } else { - device.nativeVideoWindowId = windowId + Log.e( + "$TAG TextureView for participant [${device.address.asStringUriOnly()}] wasn't initialized yet!" + ) } } } 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 02e1dc8df..7d8e9e7b7 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 @@ -40,6 +40,7 @@ import org.linphone.core.Address import org.linphone.core.AudioDevice import org.linphone.core.Call import org.linphone.core.CallListenerStub +import org.linphone.core.CallStats import org.linphone.core.ChatRoom.SecurityLevel import org.linphone.core.Core import org.linphone.core.CoreListenerStub @@ -49,6 +50,7 @@ import org.linphone.core.StreamType 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.CallStatsModel import org.linphone.ui.call.model.ConferenceModel import org.linphone.ui.main.contacts.model.ContactAvatarModel import org.linphone.ui.main.history.model.NumpadModel @@ -102,6 +104,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { val hideVideo = MutableLiveData() + val callStatsModel = CallStatsModel() + val incomingCallTitle: MutableLiveData by lazy { MutableLiveData() } @@ -198,6 +202,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { isRemoteRecordingEvent.postValue(Event(Pair(recording, displayedName.value.orEmpty()))) } + override fun onStatsUpdated(call: Call, stats: CallStats) { + callStatsModel.update(call, stats) + } + @WorkerThread override fun onStateChanged(call: Call, state: Call.State, message: String) { Log.i("$TAG Call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]") @@ -768,6 +776,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { terminatedByUsed = false currentCall = call + callStatsModel.update(call, call.audioStats) call.addListener(callListener) if (call.conference != null) { diff --git a/app/src/main/res/color/in_call_button_color.xml b/app/src/main/res/color/in_call_button_color.xml new file mode 100644 index 000000000..cfea5c512 --- /dev/null +++ b/app/src/main/res/color/in_call_button_color.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/app/src/main/res/drawable/shape_squircle_main2_400_border.xml b/app/src/main/res/drawable/shape_squircle_in_call_button_background.xml similarity index 57% rename from app/src/main/res/drawable/shape_squircle_main2_400_border.xml rename to app/src/main/res/drawable/shape_squircle_in_call_button_background.xml index a08105337..16c67b6ca 100644 --- a/app/src/main/res/drawable/shape_squircle_main2_400_border.xml +++ b/app/src/main/res/drawable/shape_squircle_in_call_button_background.xml @@ -1,5 +1,6 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/call_media_encryption_stats_menu.xml b/app/src/main/res/layout-land/call_media_encryption_stats_bottom_sheet.xml similarity index 100% rename from app/src/main/res/layout-land/call_media_encryption_stats_menu.xml rename to app/src/main/res/layout-land/call_media_encryption_stats_bottom_sheet.xml diff --git a/app/src/main/res/layout-land/call_stats_bottom_sheet.xml b/app/src/main/res/layout-land/call_stats_bottom_sheet.xml new file mode 100644 index 000000000..31c565e8d --- /dev/null +++ b/app/src/main/res/layout-land/call_stats_bottom_sheet.xml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_active_conference_fragment.xml b/app/src/main/res/layout/call_active_conference_fragment.xml index 0780301f2..4eab9a486 100644 --- a/app/src/main/res/layout/call_active_conference_fragment.xml +++ b/app/src/main/res/layout/call_active_conference_fragment.xml @@ -220,7 +220,7 @@ android:paddingBottom="12dp" android:paddingStart="20dp" android:paddingEnd="20dp" - android:background="@drawable/shape_squircle_main2_400_border" + android:background="@drawable/shape_squircle_in_call_button_background" android:text="@string/conference_share_link_title" android:textSize="18sp" android:textColor="@color/gray_main2_400" diff --git a/app/src/main/res/layout/call_active_fragment.xml b/app/src/main/res/layout/call_active_fragment.xml index fc4858fce..6e5615985 100644 --- a/app/src/main/res/layout/call_active_fragment.xml +++ b/app/src/main/res/layout/call_active_fragment.xml @@ -27,6 +27,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_audio_devices_menu.xml b/app/src/main/res/layout/call_audio_devices_bottom_sheet.xml similarity index 100% rename from app/src/main/res/layout/call_audio_devices_menu.xml rename to app/src/main/res/layout/call_audio_devices_bottom_sheet.xml diff --git a/app/src/main/res/layout/call_conference_layout_menu.xml b/app/src/main/res/layout/call_conference_layout_bottom_sheet.xml similarity index 100% rename from app/src/main/res/layout/call_conference_layout_menu.xml rename to app/src/main/res/layout/call_conference_layout_bottom_sheet.xml diff --git a/app/src/main/res/layout/call_media_encryption_stats_menu.xml b/app/src/main/res/layout/call_media_encryption_stats_bottom_sheet.xml similarity index 100% rename from app/src/main/res/layout/call_media_encryption_stats_menu.xml rename to app/src/main/res/layout/call_media_encryption_stats_bottom_sheet.xml diff --git a/app/src/main/res/layout/call_stats_bottom_sheet.xml b/app/src/main/res/layout/call_stats_bottom_sheet.xml new file mode 100644 index 000000000..12f86f295 --- /dev/null +++ b/app/src/main/res/layout/call_stats_bottom_sheet.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 b6fdd19b8..b984c22ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -548,6 +548,13 @@ Headset Headphones + Audio + Codec: %s + Bandwidth: %s + Video + Resolution: %s + FPS: %s + Media encryption: %s Post Quantum ZRTP Cipher algorithm: %s