summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-11-03 15:45:11 +0100
committerJonatan Rhodin <jonatan.rhodin@mullvad.net>2025-11-03 16:33:06 +0100
commit27491ea74e763b908546313d01164169d9f06d53 (patch)
tree488ac2ee820453fb751a40eca668aeaa4e9f5037 /android
parent03261020fb1ee4599427fc2543100da0bf1293c3 (diff)
downloadmullvadvpn-27491ea74e763b908546313d01164169d9f06d53.tar.xz
mullvadvpn-27491ea74e763b908546313d01164169d9f06d53.zip
Update include account token in problem reports
- Move checkbox below message - Add warning about anonymity - Change from AccountToken to account-token in support message
Diffstat (limited to 'android')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CheckboxCell.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ReportProblemUiStatePreviewParameterProvider.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt270
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/ClickableString.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt9
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModel.kt21
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt1
-rw-r--r--android/lib/resource/src/main/res/values/strings.xml6
-rw-r--r--android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt1
9 files changed, 232 insertions, 93 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CheckboxCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CheckboxCell.kt
index 633b32e420..75b6182039 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CheckboxCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CheckboxCell.kt
@@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import net.mullvad.mullvadvpn.lib.theme.AppTheme
@@ -31,6 +32,7 @@ internal fun CheckboxCell(
checked: Boolean,
enabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit,
+ textStyle: TextStyle = MaterialTheme.typography.bodyLarge,
background: Color = MaterialTheme.colorScheme.surfaceContainerHighest,
startPadding: Dp = Dimens.smallPadding,
endPadding: Dp = Dimens.cellEndPadding,
@@ -50,7 +52,7 @@ internal fun CheckboxCell(
Text(
text = title,
- style = MaterialTheme.typography.bodyLarge,
+ style = textStyle,
color = MaterialTheme.colorScheme.onSurface,
modifier =
Modifier.weight(1f)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ReportProblemUiStatePreviewParameterProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ReportProblemUiStatePreviewParameterProvider.kt
index f878ae9d37..b5a32dcb91 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ReportProblemUiStatePreviewParameterProvider.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/preview/ReportProblemUiStatePreviewParameterProvider.kt
@@ -11,6 +11,12 @@ class ReportProblemUiStatePreviewParameterProvider :
get() =
sequenceOf(
ReportProblemUiState(showIncludeAccountId = true),
+ ReportProblemUiState(showIncludeAccountId = true, includeAccountId = true),
+ ReportProblemUiState(
+ showIncludeAccountId = true,
+ includeAccountId = true,
+ showIncludeAccountWarningMessage = true,
+ ),
ReportProblemUiState(sendingState = SendingReportUiState.Sending),
ReportProblemUiState(sendingState = SendingReportUiState.Success("email@mail.com")),
ReportProblemUiState(
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
index ca06b04e74..28ed5e18c6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/ReportProblemScreen.kt
@@ -1,5 +1,8 @@
package net.mullvad.mullvadvpn.compose.screen
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -10,26 +13,22 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ErrorOutline
import androidx.compose.material3.Icon
-import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onSizeChanged
-import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
@@ -38,6 +37,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
@@ -55,24 +55,29 @@ import com.ramcosta.composedestinations.result.ResultRecipient
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.compose.button.VariantButton
+import net.mullvad.mullvadvpn.compose.cell.CheckboxCell
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.compose.component.ScaffoldWithMediumTopBar
+import net.mullvad.mullvadvpn.compose.extensions.createUriHook
import net.mullvad.mullvadvpn.compose.preview.ReportProblemUiStatePreviewParameterProvider
import net.mullvad.mullvadvpn.compose.textfield.mullvadWhiteTextFieldColors
import net.mullvad.mullvadvpn.compose.transitions.SlideInFromRightTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.SecureScreenWhileInView
+import net.mullvad.mullvadvpn.compose.util.clickableAnnotatedString
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
-import net.mullvad.mullvadvpn.lib.ui.designsystem.Checkbox
+import net.mullvad.mullvadvpn.lib.theme.color.warning
+import net.mullvad.mullvadvpn.lib.ui.component.ExpandChevron
+import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild
import net.mullvad.mullvadvpn.viewmodel.ReportProblemSideEffect
import net.mullvad.mullvadvpn.viewmodel.ReportProblemUiState
import net.mullvad.mullvadvpn.viewmodel.ReportProblemViewModel
import net.mullvad.mullvadvpn.viewmodel.SendingReportUiState
import org.koin.androidx.compose.koinViewModel
-@Preview("Default|Sending|Success|Error")
+@Preview("Default|IncludeAccountNumber|ShowWarning|Sending|Success|Error")
@Composable
private fun PreviewReportProblemScreen(
@PreviewParameter(ReportProblemUiStatePreviewParameterProvider::class)
@@ -87,6 +92,7 @@ private fun PreviewReportProblemScreen(
onEmailChanged = {},
onDescriptionChanged = {},
onIncludeAccountIdCheckChange = {},
+ toggleShowIncludeAccountInformationWarningMessage = {},
onBackClick = {},
)
}
@@ -126,6 +132,8 @@ fun ReportProblem(
onEmailChanged = vm::updateEmail,
onDescriptionChanged = vm::updateDescription,
onIncludeAccountIdCheckChange = vm::onIncludeAccountIdCheckChange,
+ toggleShowIncludeAccountInformationWarningMessage =
+ vm::showIncludeAccountInformationWarningMessage,
onBackClick = dropUnlessResumed { navigator.navigateUp() },
)
}
@@ -139,6 +147,7 @@ private fun ReportProblemScreen(
onEmailChanged: (String) -> Unit,
onDescriptionChanged: (String) -> Unit,
onIncludeAccountIdCheckChange: (Boolean) -> Unit,
+ toggleShowIncludeAccountInformationWarningMessage: (Boolean) -> Unit,
onBackClick: () -> Unit,
) {
@@ -172,99 +181,207 @@ private fun ReportProblemScreen(
end = Dimens.sideMargin,
bottom = Dimens.screenBottomMargin,
)
- .height(IntrinsicSize.Max),
+ .height(IntrinsicSize.Max)
+ .animateContentSize(),
verticalArrangement = Arrangement.spacedBy(Dimens.mediumPadding),
) {
- Description(
+ InputContent(
state = state,
- onIncludeAccountIdCheckChange = onIncludeAccountIdCheckChange,
- )
-
- TextField(
- modifier = Modifier.fillMaxWidth(),
- value = state.email,
- onValueChange = onEmailChanged,
- maxLines = 1,
- singleLine = true,
- placeholder = { Text(text = stringResource(id = R.string.user_email_hint)) },
- colors = mullvadWhiteTextFieldColors(),
- keyboardOptions =
- KeyboardOptions(
- autoCorrectEnabled = false,
- keyboardType = KeyboardType.Email,
- imeAction = ImeAction.Next,
- ),
- )
-
- ProblemMessageTextField(
- modifier = Modifier.weight(1f),
- value = state.description,
+ onEmailChanged = onEmailChanged,
onDescriptionChanged = onDescriptionChanged,
+ onIncludeAccountIdCheckChange = onIncludeAccountIdCheckChange,
+ toggleShowIncludeAccountInformationWarningMessage =
+ toggleShowIncludeAccountInformationWarningMessage,
+ onNavigateToViewLogs = onNavigateToViewLogs,
+ onSendReport = onSendReport,
)
-
- Column {
- PrimaryButton(
- onClick = onNavigateToViewLogs,
- text = stringResource(id = R.string.view_logs),
- )
- Spacer(modifier = Modifier.height(Dimens.buttonSpacing))
- VariantButton(
- onClick = onSendReport,
- isEnabled = state.description.isNotEmpty(),
- text = stringResource(id = R.string.send),
- )
- }
}
}
}
}
@Composable
-private fun Description(
+private fun InputContent(
state: ReportProblemUiState,
+ onEmailChanged: (String) -> Unit,
+ onDescriptionChanged: (String) -> Unit,
onIncludeAccountIdCheckChange: (Boolean) -> Unit,
+ toggleShowIncludeAccountInformationWarningMessage: (Boolean) -> Unit,
+ onNavigateToViewLogs: () -> Unit,
+ onSendReport: () -> Unit,
) {
+ Description()
+
+ TextField(
+ modifier = Modifier.fillMaxWidth(),
+ value = state.email,
+ onValueChange = onEmailChanged,
+ maxLines = 1,
+ singleLine = true,
+ placeholder = { Text(text = stringResource(id = R.string.user_email_hint)) },
+ colors = mullvadWhiteTextFieldColors(),
+ keyboardOptions =
+ KeyboardOptions(
+ autoCorrectEnabled = false,
+ keyboardType = KeyboardType.Email,
+ imeAction = ImeAction.Next,
+ ),
+ )
+
+ ProblemMessageTextField(value = state.description, onDescriptionChanged = onDescriptionChanged)
+
+ if (state.showIncludeAccountId) {
+ IncludeAccountInformationCheckBox(
+ includeAccountInformation = state.includeAccountId,
+ onIncludeAccountInformationCheckChange = onIncludeAccountIdCheckChange,
+ toggleShowIncludeAccountInformationWarningMessage =
+ toggleShowIncludeAccountInformationWarningMessage,
+ showIncludeAccountInformationWarningMessage = state.showIncludeAccountWarningMessage,
+ isPlayBuild = state.isPlayBuild,
+ )
+ }
+
+ Column {
+ PrimaryButton(
+ onClick = onNavigateToViewLogs,
+ text = stringResource(id = R.string.view_logs),
+ )
+ Spacer(modifier = Modifier.height(Dimens.buttonSpacing))
+ VariantButton(
+ onClick = onSendReport,
+ isEnabled = state.description.isNotEmpty(),
+ text = stringResource(id = R.string.send),
+ )
+ }
+}
+
+@Composable
+private fun Description() {
Column {
Text(
text = stringResource(id = R.string.problem_report_description),
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelLarge,
)
-
- if (state.showIncludeAccountId) {
- IncludeAccountInformationCheckBox(
- includeAccountInformation = state.includeAccountId,
- onIncludeAccountInformationCheckChange = onIncludeAccountIdCheckChange,
- )
- }
}
}
@Composable
private fun IncludeAccountInformationCheckBox(
includeAccountInformation: Boolean,
+ showIncludeAccountInformationWarningMessage: Boolean,
onIncludeAccountInformationCheckChange: (Boolean) -> Unit,
+ toggleShowIncludeAccountInformationWarningMessage: (Boolean) -> Unit,
+ isPlayBuild: Boolean,
) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
+ val openPrivacyPolicy =
+ LocalUriHandler.current.createUriHook(
+ stringResource(R.string.privacy_policy_url).appendHideNavOnPlayBuild(isPlayBuild)
+ )
+ Column(
modifier =
- Modifier.clickable {
- onIncludeAccountInformationCheckChange(!includeAccountInformation)
- }
- .padding(vertical = Dimens.smallPadding)
- .fillMaxWidth(),
+ Modifier.animateContentSize()
+ .border(width = Dp.Hairline, color = MaterialTheme.colorScheme.primary)
+ .padding(bottom = if (includeAccountInformation) Dimens.smallPadding else 0.dp)
) {
- // To align the checkbox with the text
- CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
- Checkbox(
- modifier = Modifier.padding(end = Dimens.smallPadding),
- checked = includeAccountInformation,
- onCheckedChange = onIncludeAccountInformationCheckChange,
+ CheckboxCell(
+ title = stringResource(R.string.include_account_token_checkbox_text),
+ checked = includeAccountInformation,
+ background = MaterialTheme.colorScheme.surface,
+ startPadding = 0.dp,
+ textStyle = MaterialTheme.typography.bodyMedium,
+ onCheckedChange = onIncludeAccountInformationCheckChange,
+ )
+ if (includeAccountInformation) {
+ AccountInformationWarning(
+ showIncludeAccountInformationWarningMessage =
+ showIncludeAccountInformationWarningMessage,
+ toggleShowIncludeAccountInformationWarningMessage =
+ toggleShowIncludeAccountInformationWarningMessage,
+ openPrivacyPolicy = openPrivacyPolicy,
+ )
+ }
+ }
+}
+
+@Composable
+private fun AccountInformationWarning(
+ showIncludeAccountInformationWarningMessage: Boolean,
+ toggleShowIncludeAccountInformationWarningMessage: (Boolean) -> Unit,
+ openPrivacyPolicy: () -> Unit,
+) {
+ Column(
+ modifier =
+ Modifier.padding(horizontal = Dimens.tinyPadding)
+ .background(MaterialTheme.colorScheme.surfaceDim)
+ .animateContentSize()
+ ) {
+ Row(
+ modifier =
+ Modifier.fillMaxWidth()
+ .clickable(
+ onClick = {
+ toggleShowIncludeAccountInformationWarningMessage(
+ !showIncludeAccountInformationWarningMessage
+ )
+ }
+ )
+ .padding(
+ top = Dimens.smallPadding,
+ start = Dimens.smallPadding,
+ end = Dimens.smallPadding,
+ bottom =
+ if (showIncludeAccountInformationWarningMessage) {
+ Dimens.tinyPadding
+ } else {
+ Dimens.smallPadding
+ },
+ )
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.ErrorOutline,
+ contentDescription = stringResource(R.string.include_account_token_warning_title),
+ tint = MaterialTheme.colorScheme.warning,
)
Text(
- text = stringResource(R.string.include_account_token_checkbox_text),
- color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier =
+ Modifier.padding(horizontal = Dimens.smallPadding)
+ .weight(1f)
+ .align(Alignment.CenterVertically),
style = MaterialTheme.typography.labelLarge,
+ text = stringResource(R.string.include_account_token_warning_title),
+ )
+ ExpandChevron(isExpanded = showIncludeAccountInformationWarningMessage)
+ }
+ if (showIncludeAccountInformationWarningMessage) {
+ Text(
+ modifier =
+ Modifier.padding(horizontal = Dimens.smallPadding)
+ .padding(bottom = Dimens.smallPadding),
+ style = MaterialTheme.typography.bodySmall,
+ text =
+ clickableAnnotatedString(
+ text =
+ buildString {
+ appendLine(
+ stringResource(
+ R.string.include_account_token_warning_message_first
+ )
+ )
+ append(
+ stringResource(
+ R.string.include_account_token_warning_message_second
+ )
+ )
+ },
+ argument = stringResource(R.string.privacy_policy_lower_case),
+ linkStyle =
+ SpanStyle(
+ color = MaterialTheme.colorScheme.onSurface,
+ textDecoration = TextDecoration.Underline,
+ ),
+ onClick = { openPrivacyPolicy() },
+ ),
)
}
}
@@ -276,23 +393,12 @@ private fun ProblemMessageTextField(
value: String,
onDescriptionChanged: (String) -> Unit,
) {
- // Stores the height of the text field after the initial onSizeChanged callback is called.
- // This size will be calculated as a weight set from the parent composable.
- var textFieldHeight by remember { mutableStateOf(0.dp) }
-
- val localDensity = LocalDensity.current
TextField(
modifier =
modifier
.fillMaxWidth()
- // Prevents the text field from shrinking when the IME is shown.
- .defaultMinSize(minHeight = if (textFieldHeight > 0.dp) textFieldHeight else 180.dp)
- // Prevents the text field from growing to large when the message is long.
- .heightIn(max = if (textFieldHeight > 0.dp) textFieldHeight else Dp.Unspecified)
- .onSizeChanged { size ->
- textFieldHeight = with(localDensity) { size.height.toDp() }
- },
+ .defaultMinSize(minHeight = Dimens.problemReportTextFieldMinHeight),
value = value,
onValueChange = onDescriptionChanged,
placeholder = { Text(stringResource(R.string.user_message_hint)) },
@@ -356,7 +462,7 @@ private fun ColumnScope.SentContent(sendingState: SendingReportUiState.Success)
val emailStart = emailTemplate.indexOf('%')
buildAnnotatedString {
- append(emailTemplate.substring(0, emailStart))
+ append(emailTemplate.take(emailStart))
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
append(sendingState.email)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/ClickableString.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/ClickableString.kt
index bcf4e1b76c..97ff867010 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/ClickableString.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/util/ClickableString.kt
@@ -24,12 +24,7 @@ fun clickableAnnotatedString(
link =
LinkAnnotation.Clickable(
tag = argument,
- linkInteractionListener =
- object : LinkInteractionListener {
- override fun onClick(link: LinkAnnotation) {
- onClick(argument)
- }
- },
+ linkInteractionListener = LinkInteractionListener { onClick(argument) },
),
block = { withStyle(style = linkStyle) { append(argument) } },
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
index 9c02c9011f..622d4f7814 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt
@@ -270,7 +270,14 @@ val uiModule = module {
viewModel { VoucherDialogViewModel(get()) }
viewModel { VpnSettingsViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { WelcomeViewModel(get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
- viewModel { ReportProblemViewModel(get(), get(), get()) }
+ viewModel {
+ ReportProblemViewModel(
+ mullvadProblemReporter = get(),
+ problemReportRepository = get(),
+ accountRepository = get(),
+ isPlayBuild = IS_PLAY_BUILD,
+ )
+ }
viewModel { ViewLogsViewModel(get()) }
viewModel { OutOfTimeViewModel(get(), get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
viewModel { FilterViewModel(get(), get()) }
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModel.kt
index f846303c09..4b2726c945 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModel.kt
@@ -26,6 +26,8 @@ data class ReportProblemUiState(
val description: String = "",
val showIncludeAccountId: Boolean = false,
val includeAccountId: Boolean = false,
+ val showIncludeAccountWarningMessage: Boolean = false,
+ val isPlayBuild: Boolean = false,
)
sealed interface SendingReportUiState {
@@ -44,30 +46,41 @@ class ReportProblemViewModel(
private val mullvadProblemReporter: MullvadProblemReport,
private val problemReportRepository: ProblemReportRepository,
accountRepository: AccountRepository,
+ private val isPlayBuild: Boolean,
) : ViewModel() {
private val sendingState: MutableStateFlow<SendingReportUiState?> = MutableStateFlow(null)
private val includeAccountIdState: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ private val showIncludeAccountWarningMessage: MutableStateFlow<Boolean> =
+ MutableStateFlow(false)
val uiState =
combine(
sendingState,
includeAccountIdState,
+ showIncludeAccountWarningMessage,
problemReportRepository.problemReport,
accountRepository.accountData,
- ) { sendingState, includeAccountToken, userReport, accountData ->
+ ) {
+ sendingState,
+ includeAccountToken,
+ showIncludeAccountWarningMessage,
+ userReport,
+ accountData ->
ReportProblemUiState(
sendingState = sendingState,
email = userReport.email ?: "",
description = userReport.description,
showIncludeAccountId = accountData != null,
includeAccountId = includeAccountToken,
+ showIncludeAccountWarningMessage = showIncludeAccountWarningMessage,
+ isPlayBuild = isPlayBuild,
)
}
.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(VIEW_MODEL_STOP_TIMEOUT),
- ReportProblemUiState(),
+ ReportProblemUiState(isPlayBuild = isPlayBuild),
)
private val _uiSideEffect = Channel<ReportProblemSideEffect>()
@@ -118,6 +131,10 @@ class ReportProblemViewModel(
includeAccountIdState.tryEmit(checked)
}
+ fun showIncludeAccountInformationWarningMessage(show: Boolean) {
+ showIncludeAccountWarningMessage.tryEmit(show)
+ }
+
private fun shouldShowConfirmNoEmail(userEmail: String?): Boolean =
userEmail.isNullOrEmpty() && uiState.value.sendingState !is SendingReportUiState
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt
index ead918124e..1fc1a49422 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ReportProblemViewModelTest.kt
@@ -45,6 +45,7 @@ class ReportProblemViewModelTest {
mockMullvadProblemReport,
mockProblemReportRepository,
mockAccountRepository,
+ isPlayBuild = false,
)
}
diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml
index 147559f0c2..68d04343d8 100644
--- a/android/lib/resource/src/main/res/values/strings.xml
+++ b/android/lib/resource/src/main/res/values/strings.xml
@@ -462,7 +462,11 @@
<string name="enable_all_methods">Enable all &amp; retry</string>
<string name="send_email">Send email</string>
<string name="no_email_app_available">No email app available on the device</string>
- <string name="include_account_token_checkbox_text">This is a question about account or payments (include account information)</string>
+ <string name="include_account_token_checkbox_text">Include my account token for faster help with payment or account related issues</string>
<string name="no_matching_relay_entry">No entry server match your settings, try changing server or other settings.</string>
<string name="no_matching_relay_exit">No exit server match your settings, try changing server or other settings.</string>
+ <string name="include_account_token_warning_title">This impacts your anonymity</string>
+ <string name="include_account_token_warning_message_first">By attaching your account token it links this report to your account, which helps us resolve your issue quicker. All reports are automatically deleted after a period of time.</string>
+ <string name="include_account_token_warning_message_second">For details, please see our %s.</string>
+ <string name="privacy_policy_lower_case">privacy policy</string>
</resources>
diff --git a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt
index 8f4426a907..6a07682d83 100644
--- a/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt
+++ b/android/lib/theme/src/main/kotlin/net/mullvad/mullvadvpn/lib/theme/dimensions/Dimensions.kt
@@ -50,6 +50,7 @@ data class Dimensions(
val obfuscationNavigationBoxWidth: Dp = 56.dp,
val outLineButtonBorderWidth: Dp = 1.dp,
val privacyPolicyIconSize: Dp = 16.dp,
+ val problemReportTextFieldMinHeight: Dp = 220.dp,
val reconnectButtonMinInteractiveComponentSize: Dp = 40.dp,
val relayCircleSize: Dp = 16.dp,
val relayCirclePadding: Dp = 8.dp,