Add the call quality diagnostics fragment.

This commit is contained in:
Alex Hart
2026-01-12 14:50:04 -04:00
committed by Michelle Tang
parent 5759609a11
commit ed12a7691d
6 changed files with 163 additions and 11 deletions

View File

@@ -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)
}

View File

@@ -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() }
)
}
}

View File

@@ -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()
)
}
}

View File

@@ -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))
}
}

View File

@@ -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<CallQualityIssue>)
@@ -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<CallQualityIssue>) = Unit