summaryrefslogtreecommitdiffhomepage
path: root/android/lib
diff options
context:
space:
mode:
Diffstat (limited to 'android/lib')
-rw-r--r--android/lib/feature/settings/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/api/FaqRemoteNavKey.kt6
-rw-r--r--android/lib/feature/settings/impl/build.gradle.kts7
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteScreen.kt48
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteViewModel.kt53
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt25
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/FaqRemoteEntryProvider.kt13
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/SettingsEntryProvider.kt2
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Downloader.kt5
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Parser.kt186
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Server.kt67
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqBlock.kt10
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqItem.kt3
-rw-r--r--android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/RichText.kt10
13 files changed, 432 insertions, 3 deletions
diff --git a/android/lib/feature/settings/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/api/FaqRemoteNavKey.kt b/android/lib/feature/settings/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/api/FaqRemoteNavKey.kt
new file mode 100644
index 0000000000..161add6623
--- /dev/null
+++ b/android/lib/feature/settings/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/api/FaqRemoteNavKey.kt
@@ -0,0 +1,6 @@
+package net.mullvad.mullvadvpn.feature.settings.api
+
+import kotlinx.parcelize.Parcelize
+import net.mullvad.mullvadvpn.core.NavKey2
+
+@Parcelize data object FaqRemoteNavKey : NavKey2
diff --git a/android/lib/feature/settings/impl/build.gradle.kts b/android/lib/feature/settings/impl/build.gradle.kts
index a7d325b986..8c9d171e6f 100644
--- a/android/lib/feature/settings/impl/build.gradle.kts
+++ b/android/lib/feature/settings/impl/build.gradle.kts
@@ -25,4 +25,11 @@ dependencies {
implementation(libs.koin.compose)
implementation(libs.arrow)
implementation(libs.protobuf.kotlin.lite)
+ implementation(libs.jsoup)
+ implementation(libs.remote.compose.creation.compose)
+ implementation(libs.remote.compose.player.core)
+ implementation(libs.remote.compose.player.compose)
+ implementation(libs.remote.compose.core)
+ implementation(libs.remote.compose.creation.core)
+ implementation(libs.remote.compose.creation.android)
}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteScreen.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteScreen.kt
new file mode 100644
index 0000000000..2c8bb8cbad
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteScreen.kt
@@ -0,0 +1,48 @@
+package net.mullvad.mullvadvpn.feature.settings.impl
+
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.remote.player.compose.RemoteDocumentPlayer
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.res.stringResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.compose.dropUnlessResumed
+import net.mullvad.mullvadvpn.core.Navigator
+import net.mullvad.mullvadvpn.lib.common.Lc
+import net.mullvad.mullvadvpn.lib.ui.component.NavigateBackIconButton
+import net.mullvad.mullvadvpn.lib.ui.component.ScaffoldWithSmallTopBar
+import net.mullvad.mullvadvpn.lib.ui.designsystem.MullvadCircularProgressIndicatorLarge
+import org.koin.androidx.compose.koinViewModel
+
+@Composable
+fun FaqRemote(navigator: Navigator) {
+ val viewmodel = koinViewModel<FaqRemoteViewModel>()
+ val state by viewmodel.uiState.collectAsStateWithLifecycle()
+ FaqRemoteScreen(state = state, onBackClick = dropUnlessResumed { navigator.goBack() })
+}
+
+@Composable
+private fun FaqRemoteScreen(state: Lc<Unit, FaqRemoteState>, onBackClick: () -> Unit) {
+ ScaffoldWithSmallTopBar(
+ appBarTitle = stringResource(R.string.faqs_and_guides),
+ navigationIcon = { NavigateBackIconButton(onNavigateBack = onBackClick) },
+ ) { modifier ->
+ BoxWithConstraints(modifier = modifier) {
+ val screenWidth = constraints.maxWidth
+ val screenHeight = constraints.maxHeight
+
+ when (state) {
+ is Lc.Loading -> {
+ MullvadCircularProgressIndicatorLarge()
+ }
+ is Lc.Content -> {
+ RemoteDocumentPlayer(
+ document = state.value.document,
+ documentWidth = screenWidth,
+ documentHeight = screenHeight,
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteViewModel.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteViewModel.kt
new file mode 100644
index 0000000000..ba25cc4bfc
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/FaqRemoteViewModel.kt
@@ -0,0 +1,53 @@
+package net.mullvad.mullvadvpn.feature.settings.impl
+
+import androidx.compose.remote.core.CoreDocument
+import androidx.compose.remote.creation.RemoteComposeWriter
+import androidx.compose.remote.creation.compose.capture.RemoteComposeCapture
+import androidx.compose.remote.creation.compose.widgets.RemoteComposeWidget
+import androidx.compose.remote.player.core.RemoteDocument
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import co.touchlab.kermit.Logger
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.WhileSubscribed
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.feature.settings.impl.server.Server
+import net.mullvad.mullvadvpn.lib.common.Lc
+import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
+
+data class FaqRemoteState(val document: CoreDocument)
+
+class FaqRemoteViewModel(private val server: Server) : ViewModel() {
+
+ private val document = MutableStateFlow<CoreDocument?>(null)
+
+ val uiState =
+ document
+ .filterNotNull()
+ .map { Lc.Content(FaqRemoteState(document = it)) }
+ .stateIn(
+ viewModelScope,
+ SharingStarted.WhileSubscribed(VIEW_MODEL_STOP_TIMEOUT),
+ Lc.Loading(Unit),
+ )
+
+ init {
+ viewModelScope.launch(Dispatchers.IO) {
+ val remoteDocument = server.main()
+ //val bytes = remoteDocument.bytes
+
+ //val playerDocument = CoreDocument()
+ Logger.d { "remoteDocument:$remoteDocument" }
+
+ //playerDocument.
+ //document.value = playerDocument
+ //RemoteComposeCapture
+ document.value = RemoteDocument(remoteDocument.bytes).document
+ }
+ }
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
index 7d72d706fe..e8cab9953b 100644
--- a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/SettingsScreen.kt
@@ -30,6 +30,7 @@ import net.mullvad.mullvadvpn.feature.daita.api.DaitaNavKey
import net.mullvad.mullvadvpn.feature.multihop.api.MultihopNavKey
import net.mullvad.mullvadvpn.feature.notification.api.NotificationSettingsNavKey
import net.mullvad.mullvadvpn.feature.problemreport.api.ProblemReportNavKey
+import net.mullvad.mullvadvpn.feature.settings.api.FaqRemoteNavKey
import net.mullvad.mullvadvpn.feature.splittunneling.api.SplitTunnelingNavKey
import net.mullvad.mullvadvpn.feature.vpnsettings.api.VpnSettingsNavKey
import net.mullvad.mullvadvpn.lib.common.Lc
@@ -68,6 +69,8 @@ private fun PreviewSettingsScreen(
onDaitaClick = {},
onBackClick = {},
onNotificationSettingsCellClick = {},
+ onAppObfuscationClick = {},
+ onFaqClick = {}
)
}
}
@@ -91,6 +94,7 @@ fun Settings(navigator: Navigator) {
onNotificationSettingsCellClick =
dropUnlessResumed { navigator.navigate(NotificationSettingsNavKey) },
onAppObfuscationClick = dropUnlessResumed { navigator.navigate(AppearanceNavKey) },
+ onFaqClick = dropUnlessResumed { navigator.navigate(FaqRemoteNavKey) }
)
}
@@ -106,7 +110,8 @@ fun SettingsScreen(
onDaitaClick: () -> Unit,
onBackClick: () -> Unit,
onNotificationSettingsCellClick: () -> Unit,
- onAppObfuscationClick: () -> Unit = {},
+ onAppObfuscationClick: () -> Unit,
+ onFaqClick: () -> Unit,
) {
ScaffoldWithMediumTopBar(
appBarTitle = stringResource(id = R.string.settings),
@@ -135,6 +140,7 @@ fun SettingsScreen(
onDaitaClick = onDaitaClick,
onNotificationSettingsCellClick = onNotificationSettingsCellClick,
onAppObfuscationClick = onAppObfuscationClick,
+ onFaqClick = onFaqClick,
)
}
}
@@ -152,7 +158,8 @@ private fun LazyListScope.content(
onMultihopClick: () -> Unit,
onDaitaClick: () -> Unit,
onNotificationSettingsCellClick: () -> Unit,
- onAppObfuscationClick: () -> Unit = {},
+ onAppObfuscationClick: () -> Unit,
+ onFaqClick: () -> Unit,
) {
if (state.isLoggedIn) {
itemWithDivider {
@@ -208,7 +215,9 @@ private fun LazyListScope.content(
itemWithDivider { ReportProblem(onReportProblemCellClick) }
- if (!state.isPlayBuild) {
+ if (state.isPlayBuild) {
+ itemWithDivider { RemoteFaq(onClick = onFaqClick) }
+ } else {
itemWithDivider { FaqAndGuides() }
}
@@ -260,6 +269,16 @@ private fun FaqAndGuides() {
}
@Composable
+private fun RemoteFaq(onClick: () -> Unit) {
+ val faqGuideLabel = stringResource(id = R.string.faqs_and_guides)
+ NavigationListItem(
+ title = faqGuideLabel,
+ onClick = onClick,
+ position = Position.Middle,
+ )
+}
+
+@Composable
private fun PrivacyPolicy(state: SettingsUiState) {
val privacyPolicyLabel = stringResource(id = R.string.privacy_policy_label)
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/FaqRemoteEntryProvider.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/FaqRemoteEntryProvider.kt
new file mode 100644
index 0000000000..7107573a45
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/FaqRemoteEntryProvider.kt
@@ -0,0 +1,13 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.navigation
+
+import androidx.navigation3.runtime.EntryProviderScope
+import net.mullvad.mullvadvpn.core.NavKey2
+import net.mullvad.mullvadvpn.core.Navigator
+import net.mullvad.mullvadvpn.feature.settings.api.FaqRemoteNavKey
+import net.mullvad.mullvadvpn.feature.settings.impl.FaqRemote
+
+internal fun EntryProviderScope<NavKey2>.faqRemoteEntry(navigator: Navigator) {
+ entry<FaqRemoteNavKey>() {
+ FaqRemote(navigator = navigator)
+ }
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/SettingsEntryProvider.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/SettingsEntryProvider.kt
index fe9f5660d5..2fae30f986 100644
--- a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/SettingsEntryProvider.kt
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/navigation/SettingsEntryProvider.kt
@@ -9,4 +9,6 @@ import net.mullvad.mullvadvpn.feature.settings.impl.Settings
fun EntryProviderScope<NavKey2>.settingsEntry(navigator: Navigator) {
entry<SettingsNavKey>(metadata = topLevelTransition()) { Settings(navigator = navigator) }
+
+ faqRemoteEntry(navigator)
}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Downloader.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Downloader.kt
new file mode 100644
index 0000000000..e474b51397
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Downloader.kt
@@ -0,0 +1,5 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server
+
+import org.jsoup.Jsoup
+
+suspend fun download() = Jsoup.connect("https://mullvad.net/en/help/faq").get()
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Parser.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Parser.kt
new file mode 100644
index 0000000000..50e74c274c
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Parser.kt
@@ -0,0 +1,186 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server
+
+import kotlin.text.indexOf
+import net.mullvad.mullvadvpn.feature.settings.impl.server.model.FaqBlock
+import net.mullvad.mullvadvpn.feature.settings.impl.server.model.FaqItem
+import net.mullvad.mullvadvpn.feature.settings.impl.server.model.RichText
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.TextNode
+
+fun parseFaq(doc: Document): List<FaqBlock.Question> {
+ val article = doc.selectFirst("main article") ?: return emptyList()
+
+ val result = mutableListOf<FaqBlock.Question>()
+
+ var currentTitle: String? = null
+ val currentContent = mutableListOf<FaqBlock.Content>()
+
+ for (el in article.children()) {
+ when (el.tagName()) {
+
+ "h2" -> {
+ // flush previous question
+ if (currentTitle != null) {
+ result += FaqBlock.Question(currentTitle, currentContent.toList())
+ currentContent.clear()
+ }
+ currentTitle = el.text()
+ }
+
+ "p" -> {
+ currentContent += FaqBlock.Content.Paragraph(el.text())
+ }
+
+ "ul" -> {
+ el.select("li").forEach { li ->
+ currentContent += FaqBlock.Content.ListItem(li.text())
+ }
+ }
+ }
+ }
+
+ // flush last item
+ if (currentTitle != null) {
+ result += FaqBlock.Question(currentTitle, currentContent.toList())
+ }
+
+ return result
+}
+
+fun parseFaqFromText(doc: org.jsoup.nodes.Document): List<FaqItem> {
+ val rawText = doc.body().text()
+
+ // Split by question marks + whitespace
+ val parts = rawText.split(Regex("(?<=\\?)(\\s+|$)"))
+
+ val items = mutableListOf<FaqItem>()
+
+ var i = 0
+ while (i < parts.size) {
+ val question = parts[i].trim()
+ val answer = if (i + 1 < parts.size) parts[i + 1].trim() else ""
+ items += FaqItem(question, answer)
+ i += 2
+ }
+
+ return items
+}
+
+fun parseFaqClean(doc: org.jsoup.nodes.Document): List<FaqItem> {
+ val text = doc.body().text()
+
+ // Find where the FAQ really starts
+ val faqStartIndex = Regex("\\?").find(text)?.range?.first ?: 0
+
+ val trimmedText = text.substring(faqStartIndex)
+
+ // Split by question/answer boundaries
+ val parts = trimmedText.split(Regex("(?<=\\?).*?(?=\\S)"))
+
+ val items = mutableListOf<FaqItem>()
+ var i = 0
+ while (i < parts.size - 1) {
+ val question = parts[i].trim()
+ val answer = parts[i + 1].trim()
+ items += FaqItem(question, answer)
+ i += 2
+ }
+ return items
+}
+
+fun parseFaqByHash(doc: Document): List<FaqItem> {
+ val body = doc.body()
+
+ // Collect all text lines
+ val lines = body.text().lines()
+
+ val items = mutableListOf<FaqItem>()
+ var currentQuestion: String? = null
+ val currentAnswer = StringBuilder()
+
+ for (line in lines) {
+ val trimmed = line.trim()
+ if (trimmed.startsWith("#")) {
+ // Save previous question/answer
+ if (currentQuestion != null) {
+ items += FaqItem(currentQuestion, currentAnswer.toString().trim())
+ currentAnswer.clear()
+ }
+ // New question (remove leading #)
+ currentQuestion = trimmed.removePrefix("#").trim()
+ } else {
+ // Append to current answer
+ if (currentQuestion != null) {
+ if (currentAnswer.isNotEmpty()) currentAnswer.append("\n")
+ currentAnswer.append(trimmed)
+ }
+ }
+ }
+
+ // Add the last question
+ if (currentQuestion != null) {
+ items += FaqItem(currentQuestion, currentAnswer.toString().trim())
+ }
+
+ return items
+}
+
+fun parseMullvadFaq(doc: Document): List<FaqItem> {
+ val items = mutableListOf<FaqItem>()
+
+ // 1. Mullvad FAQ questions are contained within <h3> tags
+ val questionHeaders = doc.select("h3")
+
+ for (header in questionHeaders) {
+ val question = header.text().trim()
+ val answerBuilder = StringBuilder()
+
+ // 2. The answer consists of all siblings (p, ul, div)
+ // until we hit the next <h3> or the end of the section.
+ var sibling = header.nextElementSibling()
+
+ while (sibling != null && sibling.tagName() != "h3" && sibling.tagName() != "h2") {
+ // Append the text of the sibling (paragraph, list item, etc.)
+ val text = sibling.text().trim()
+ if (text.isNotEmpty()) {
+ if (answerBuilder.isNotEmpty()) answerBuilder.append("\n\n")
+ answerBuilder.append(text)
+ }
+ sibling = sibling.nextElementSibling()
+ }
+
+ val answer = answerBuilder.toString().trim()
+
+ if (question.isNotEmpty() && answer.isNotEmpty()) {
+ items.add(FaqItem(question, answer))
+ }
+ }
+
+ return items
+}
+
+fun parseParagraph(el: Element): FaqBlock.Content.Paragraph {
+ val parts = mutableListOf<RichText.Part>()
+
+ el.childNodes().forEach { node ->
+ when (node) {
+ is TextNode -> parts += RichText.Part.Text(node.text())
+ is Element -> {
+ if (node.tagName() == "a") {
+ parts += RichText.Part.Link(
+ node.text(),
+ node.absUrl("href")
+ )
+ }
+ }
+ }
+ }
+
+ return FaqBlock.Content.Paragraph(parts.joinToString("") {
+ when (it) {
+ is RichText.Part.Text -> it.text
+ is RichText.Part.Link -> it.text // simple fallback
+ }
+ })
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Server.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Server.kt
new file mode 100644
index 0000000000..472928af71
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/Server.kt
@@ -0,0 +1,67 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server
+
+import android.content.Context
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.remote.creation.compose.capture.CapturedDocument
+import androidx.compose.remote.creation.compose.capture.captureSingleRemoteDocument
+import androidx.compose.remote.creation.compose.layout.RemoteArrangement
+import androidx.compose.remote.creation.compose.layout.RemoteColumn
+import androidx.compose.remote.creation.compose.layout.RemoteText
+import androidx.compose.remote.creation.compose.state.RemoteDp
+import androidx.compose.remote.creation.compose.state.asRemoteTextUnit
+import androidx.compose.remote.creation.compose.state.rc
+import androidx.compose.remote.creation.compose.text.RemoteTextStyle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import co.touchlab.kermit.Logger
+import net.mullvad.mullvadvpn.feature.settings.impl.server.model.FaqItem
+
+class Server(private val context: Context) {
+ suspend fun main(): CapturedDocument {
+ val document = download()
+ val data = parseFaqClean(document)
+ val captured = captureSingleRemoteDocument(context = context) { RemoteFaq(faqItems = data) }
+ return captured
+ }
+}
+
+@Composable
+fun RemoteFaq(faqItems: List<FaqItem>) {
+ RemoteColumn(verticalArrangement = RemoteArrangement.spacedBy(RemoteDp(10.dp))) {
+ RemoteText(
+ "These are FAQs",
+ color = MaterialTheme.colorScheme.onPrimary.rc,
+ fontSize = MaterialTheme.typography.titleLarge.fontSize.asRemoteTextUnit(),
+ )
+ Logger.d { "questions=$faqItems" }
+ faqItems.forEach { q ->
+ RemoteText(
+ text = "Section",
+ style = RemoteTextStyle.Default,
+ color = MaterialTheme.colorScheme.onPrimary.rc,
+ )
+
+ RemoteText(
+ text = q.question,
+ style = RemoteTextStyle.Default,
+ color = MaterialTheme.colorScheme.onPrimary.rc,
+ )
+
+ RemoteText(
+ text = q.answer,
+ style = RemoteTextStyle.Default,
+ color = MaterialTheme.colorScheme.onPrimary.rc,
+ )
+
+ /*q.content.forEach { c ->
+ when (c) {
+ is FaqBlock.Content.Paragraph ->
+ RemoteText(c.text, color = MaterialTheme.colorScheme.onPrimary.rc)
+
+ is FaqBlock.Content.ListItem ->
+ RemoteText("• ${c.text}", color = MaterialTheme.colorScheme.onPrimary.rc)
+ }
+ }*/
+ }
+ }
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqBlock.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqBlock.kt
new file mode 100644
index 0000000000..f71fa328bf
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqBlock.kt
@@ -0,0 +1,10 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server.model
+
+sealed class FaqBlock {
+ data class Question(val title: String, val content: List<Content>) : FaqBlock()
+
+ sealed class Content {
+ data class Paragraph(val text: String) : Content()
+ data class ListItem(val text: String) : Content()
+ }
+}
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqItem.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqItem.kt
new file mode 100644
index 0000000000..65aeac5529
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/FaqItem.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server.model
+
+data class FaqItem(val question: String, val answer: String)
diff --git a/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/RichText.kt b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/RichText.kt
new file mode 100644
index 0000000000..aa73ea99f2
--- /dev/null
+++ b/android/lib/feature/settings/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/settings/impl/server/model/RichText.kt
@@ -0,0 +1,10 @@
+package net.mullvad.mullvadvpn.feature.settings.impl.server.model
+
+data class RichText(
+ val parts: List<Part>
+) {
+ sealed class Part {
+ data class Text(val text: String) : Part()
+ data class Link(val text: String, val url: String) : Part()
+ }
+}