From ed12a7691de12ef09145f5b0f2f8be5afe2a9056 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Mon, 12 Jan 2026 14:50:04 -0400 Subject: [PATCH] Add the call quality diagnostics fragment. --- .../quality/CallQualityBottomSheetFragment.kt | 6 ++ .../quality/CallQualityDiagnosticsFragment.kt | 37 +++++++++ .../quality/CallQualityDiagnosticsScreen.kt | 79 +++++++++++++++++++ .../quality/CallQualityScreenViewModel.kt | 8 +- .../calls/quality/CallQualityScreens.kt | 26 +++++- app/src/main/res/values/strings.xml | 18 ++++- 6 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsFragment.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsScreen.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityBottomSheetFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityBottomSheetFragment.kt index dd493dd392..74a865141d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityBottomSheetFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityBottomSheetFragment.kt @@ -76,6 +76,12 @@ class CallQualityBottomSheetFragment : ComposeBottomSheetDialogFragment() { ) } + override fun viewDiagnostics() { + CallQualityDiagnosticsFragment.create( + viewModel.getRequestSnapshot() + ).show(parentFragmentManager, null) + } + override fun onUserSatisfiedWithCall(isUserSatisfiedWithCall: Boolean) { viewModel.setUserSatisfiedWithCall(isUserSatisfiedWithCall) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsFragment.kt new file mode 100644 index 0000000000..a8a8ccb63a --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsFragment.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.calls.quality + +import androidx.compose.runtime.Composable +import androidx.core.os.bundleOf +import org.signal.storageservice.protos.calls.quality.SubmitCallQualitySurveyRequest +import org.thoughtcrime.securesms.compose.ComposeFullScreenDialogFragment + +class CallQualityDiagnosticsFragment : ComposeFullScreenDialogFragment() { + + companion object { + private const val REQUEST_KEY = "CallQualityDiagnosticsRequestKey" + + fun create(request: SubmitCallQualitySurveyRequest): CallQualityDiagnosticsFragment { + return CallQualityDiagnosticsFragment().apply { + arguments = bundleOf(REQUEST_KEY to request.encode()) + } + } + } + + private val callQualitySurveyRequest: SubmitCallQualitySurveyRequest by lazy { + val bytes = requireArguments().getByteArray(REQUEST_KEY)!! + SubmitCallQualitySurveyRequest.ADAPTER.decode(bytes) + } + + @Composable + override fun DialogContent() { + CallQualityDiagnosticsScreen( + callQualitySurveyRequest = callQualitySurveyRequest, + onNavigationClick = { requireActivity().onBackPressedDispatcher.onBackPressed() } + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsScreen.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsScreen.kt new file mode 100644 index 0000000000..4818236407 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityDiagnosticsScreen.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.calls.quality + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.unit.dp +import org.signal.core.ui.compose.Buttons +import org.signal.core.ui.compose.DayNightPreviews +import org.signal.core.ui.compose.Previews +import org.signal.core.ui.compose.Scaffolds +import org.signal.core.ui.compose.horizontalGutters +import org.signal.storageservice.protos.calls.quality.SubmitCallQualitySurveyRequest +import org.thoughtcrime.securesms.R + +@Composable +fun CallQualityDiagnosticsScreen( + callQualitySurveyRequest: SubmitCallQualitySurveyRequest, + onNavigationClick: () -> Unit = {} +) { + Scaffolds.Settings( + title = stringResource(R.string.CallQualityDiagnosticsScreen__diagnostic_information), + navigationIcon = ImageVector.vectorResource(R.drawable.symbol_arrow_start_24), + navigationContentDescription = stringResource(R.string.CallQualityDiagnosticsScreen__close), + onNavigationClick = onNavigationClick + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .padding(it) + .fillMaxSize() + .horizontalGutters() + ) { + Box( + modifier = Modifier + .weight(1f) + .verticalScroll(rememberScrollState()) + ) { + Text( + text = callQualitySurveyRequest.toString().substringAfter(SubmitCallQualitySurveyRequest::class.simpleName ?: "") + ) + } + + Buttons.LargeTonal( + onClick = onNavigationClick, + modifier = Modifier + .padding(top = 10.dp, bottom = 24.dp) + .widthIn(min = 256.dp) + ) { + Text(text = stringResource(R.string.CallQualityDiagnosticsScreen__close)) + } + } + } +} + +@DayNightPreviews +@Composable +private fun CallQualityDiagnosticsScreenPreview() { + Previews.Preview { + CallQualityDiagnosticsScreen( + callQualitySurveyRequest = SubmitCallQualitySurveyRequest() + ) + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreenViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreenViewModel.kt index ab0ca97e28..8811e171fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreenViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreenViewModel.kt @@ -58,6 +58,10 @@ class CallQualityScreenViewModel( return } + AppDependencies.jobManager.add(CallQualitySurveySubmissionJob(getRequestSnapshot(), state.value.isShareDebugLogSelected)) + } + + fun getRequestSnapshot(): SubmitCallQualitySurveyRequest { val stateSnapshot = state.value val somethingElseDescription: String? = if (stateSnapshot.selectedQualityIssues.contains(CallQualityIssue.SOMETHING_ELSE)) { stateSnapshot.somethingElseDescription.takeIf { it.isNotEmpty() } @@ -65,12 +69,10 @@ class CallQualityScreenViewModel( null } - val requestToSubmitToJob = initialRequest.newBuilder() + return initialRequest.newBuilder() .user_satisfied(stateSnapshot.isUserSatisfiedWithCall) .call_quality_issues(stateSnapshot.selectedQualityIssues.map { it.code }) .additional_issues_description(somethingElseDescription) .build() - - AppDependencies.jobManager.add(CallQualitySurveySubmissionJob(requestToSubmitToJob, stateSnapshot.isShareDebugLogSelected)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreens.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreens.kt index d5b3e463bd..95a664a8b0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreens.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/quality/CallQualityScreens.kt @@ -141,6 +141,7 @@ fun CallQualitySheet( CallQualitySheetNavEntry.HelpUsImprove -> HelpUsImprove( isShareDebugLogSelected = state.isShareDebugLogSelected, onViewDebugLogClick = callback::viewDebugLog, + onViewDiagnosticsClick = callback::viewDiagnostics, onCancelClick = callback::dismiss, onShareDebugLogChanged = callback::onShareDebugLogChanged, onSubmitClick = callback::submit @@ -431,6 +432,7 @@ private fun IssueChip( private fun HelpUsImprove( isShareDebugLogSelected: Boolean, onShareDebugLogChanged: (Boolean) -> Unit, + onViewDiagnosticsClick: () -> Unit, onViewDebugLogClick: () -> Unit, onCancelClick: () -> Unit, onSubmitClick: () -> Unit @@ -443,12 +445,12 @@ private fun HelpUsImprove( withLink( link = LinkAnnotation.Clickable( - "view-your-debug-log", - linkInteractionListener = { onViewDebugLogClick() }, + "view-diagnostics", + linkInteractionListener = { onViewDiagnosticsClick() }, styles = TextLinkStyles(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) ) ) { - append(stringResource(R.string.CallQualitySheet__view_your_debug_log)) + append(stringResource(R.string.CallQualitySheet__diagnostic_information_about_your_call)) } append(" ") @@ -463,7 +465,20 @@ private fun HelpUsImprove( ) Text( - text = stringResource(R.string.CallQualitySheet__debug_log_privacy_notice), + text = buildAnnotatedString { + append(stringResource(R.string.CallQualitySheet__information_shared_with_us)) + append(" ") + + withLink( + link = LinkAnnotation.Clickable( + "view-debug-log", + linkInteractionListener = { onViewDebugLogClick() }, + styles = TextLinkStyles(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) + ) + ) { + append(stringResource(R.string.CallQualitySheet__view_debug_log)) + } + }, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier @@ -688,6 +703,7 @@ private fun HelpUsImprovePreview() { Column { HelpUsImprove( isShareDebugLogSelected = true, + onViewDiagnosticsClick = {}, onViewDebugLogClick = {}, onCancelClick = {}, onShareDebugLogChanged = {}, @@ -728,6 +744,7 @@ data class CallQualitySheetState( interface CallQualitySheetCallback { fun dismiss() fun viewDebugLog() + fun viewDiagnostics() fun onUserSatisfiedWithCall(isUserSatisfiedWithCall: Boolean) fun describeYourIssue() fun onCallQualityIssueSelectionChanged(selection: Set) @@ -738,6 +755,7 @@ interface CallQualitySheetCallback { object Empty : CallQualitySheetCallback { override fun dismiss() = Unit override fun viewDebugLog() = Unit + override fun viewDiagnostics() = Unit override fun onUserSatisfiedWithCall(isUserSatisfiedWithCall: Boolean) = Unit override fun describeYourIssue() = Unit override fun onCallQualityIssueSelectionChanged(selection: Set) = Unit diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd2ba0f871..297ec460b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2705,6 +2705,12 @@ Switch camera direction + + + Diagnostic information + + Close + Swipe to view screen share @@ -9037,11 +9043,15 @@ Help us improve - Sending us your diagnostics info helps us improve call quality. You can + Submitting will share your feedback along with + + diagnostic information about your call + + You can optionally share a debug log to help us improve call quality. + + Information shared with us contains low level app information and does not include the contents of your calls. - view your debug log - - before submitting. + View debug log Share debug log