summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2024-06-13 13:35:27 +0200
committerAlbin <albin@mullvad.net>2024-06-13 17:01:00 +0200
commitb43b4c517ee42d438e7189bfa033af8109f19e94 (patch)
treeb8d8cd66f70453577323e1238cc617bbcdb07d34
parent39bc2d98d771b5a8745f530124cf6802080abd10 (diff)
downloadmullvadvpn-b43b4c517ee42d438e7189bfa033af8109f19e94.tar.xz
mullvadvpn-b43b4c517ee42d438e7189bfa033af8109f19e94.zip
Clarify lockdown limitations in guide
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt131
-rw-r--r--android/lib/resource/src/main/res/values/strings.xml5
-rw-r--r--android/lib/resource/src/main/res/values/strings_non_translatable.xml2
3 files changed, 108 insertions, 30 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt
index f4d417e6f3..5a01e4c941 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt
@@ -1,5 +1,7 @@
package net.mullvad.mullvadvpn.compose.screen
+import android.net.Uri
+import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -16,6 +18,7 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -31,8 +34,14 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.UrlAnnotation
+import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextDecoration
+import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.constraintlayout.compose.ConstrainedLayoutReference
import androidx.constraintlayout.compose.ConstraintLayout
@@ -46,12 +55,15 @@ import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
import net.mullvad.mullvadvpn.compose.component.ScaffoldWithLargeTopBarAndButton
import net.mullvad.mullvadvpn.compose.extensions.toAnnotatedString
import net.mullvad.mullvadvpn.compose.transitions.SlideInFromRightTransition
+import net.mullvad.mullvadvpn.lib.common.util.openLink
import net.mullvad.mullvadvpn.lib.common.util.openVpnSettings
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaDescription
import net.mullvad.mullvadvpn.lib.theme.color.AlphaInvisible
import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible
+import net.mullvad.mullvadvpn.service.constant.IS_PLAY_BUILD
+import net.mullvad.mullvadvpn.util.appendHideNavOnPlayBuild
@Preview
@Composable
@@ -87,7 +99,8 @@ fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) {
pagerState = pagerState,
backButtonRef = backButtonRef,
nextButtonRef = nextButtonRef,
- pager = pager
+ pager = pager,
+ onOpenUrl = { url -> context.openLink(Uri.parse(url)) }
)
// Go to previous page
@@ -131,17 +144,18 @@ fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) {
)
}
}
- }
+ },
)
}
-@OptIn(ExperimentalFoundationApi::class)
+@OptIn(ExperimentalFoundationApi::class, ExperimentalTextApi::class)
@Composable
private fun ConstraintLayoutScope.AutoConnectCarousel(
pagerState: PagerState,
backButtonRef: ConstrainedLayoutReference,
nextButtonRef: ConstrainedLayoutReference,
- pager: ConstrainedLayoutReference
+ pager: ConstrainedLayoutReference,
+ onOpenUrl: (String) -> Unit
) {
HorizontalPager(
state = pagerState,
@@ -152,32 +166,27 @@ private fun ConstraintLayoutScope.AutoConnectCarousel(
start.linkTo(backButtonRef.end)
end.linkTo(nextButtonRef.start)
bottom.linkTo(parent.bottom)
- }
- ) { page ->
+ },
+ ) { pageIndex ->
+ val page = PAGES.entries[pageIndex]
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
- Text(
+ val annotatedTopText = page.annotatedTopText()
+ ClickableText(
modifier = Modifier.padding(horizontal = Dimens.largePadding),
style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSecondary,
- text =
- HtmlCompat.fromHtml(
- stringResource(id = PAGES.entries[page].topText),
- HtmlCompat.FROM_HTML_MODE_COMPACT
- )
- .toAnnotatedString(
- boldSpanStyle =
- SpanStyle(
- fontWeight = FontWeight.ExtraBold,
- color = MaterialTheme.colorScheme.onPrimary
- )
- )
+ text = annotatedTopText,
+ onClick = {
+ annotatedTopText.getUrlAnnotations(it, it).let { annotation ->
+ annotation.firstOrNull()?.item?.url?.let { onOpenUrl(it) }
+ }
+ },
)
Image(
modifier = Modifier.padding(top = Dimens.topPadding, bottom = Dimens.bottomPadding),
- painter = painterResource(id = PAGES.entries[page].image),
+ painter = painterResource(id = page.image),
contentDescription = null,
)
Text(
@@ -186,7 +195,7 @@ private fun ConstraintLayoutScope.AutoConnectCarousel(
color = MaterialTheme.colorScheme.onSecondary,
text =
HtmlCompat.fromHtml(
- stringResource(id = PAGES.entries[page].bottomText),
+ stringResource(id = page.bottomText),
HtmlCompat.FROM_HTML_MODE_COMPACT
)
.toAnnotatedString(
@@ -255,20 +264,86 @@ private fun ConstraintLayoutScope.PageIndicator(
}
}
-private enum class PAGES(val topText: Int, val image: Int, val bottomText: Int) {
+@Composable
+private fun buildTopText(@StringRes id: Int) = buildAnnotatedString {
+ withStyle(
+ style = SpanStyle(color = MaterialTheme.colorScheme.onSecondary),
+ ) {
+ append(
+ HtmlCompat.fromHtml(stringResource(id = id), HtmlCompat.FROM_HTML_MODE_COMPACT)
+ .toAnnotatedString(
+ boldSpanStyle =
+ SpanStyle(
+ fontWeight = FontWeight.ExtraBold,
+ color = MaterialTheme.colorScheme.onPrimary
+ )
+ )
+ )
+ }
+}
+
+@OptIn(ExperimentalTextApi::class)
+@Composable
+private fun buildLockdownTopText() = buildAnnotatedString {
+ append(buildTopText(id = R.string.auto_connect_carousel_third_slide_top_text))
+ append(" ")
+
+ withLink(
+ UrlAnnotation(
+ stringResource(id = R.string.lockdown_url).appendHideNavOnPlayBuild(IS_PLAY_BUILD)
+ )
+ ) {
+ withStyle(
+ style =
+ SpanStyle(
+ color = MaterialTheme.colorScheme.onPrimary,
+ textDecoration = TextDecoration.Underline
+ ),
+ ) {
+ append(
+ stringResource(
+ id = R.string.auto_connect_carousel_third_slide_top_text_website_link
+ )
+ )
+ append(".")
+ }
+ }
+}
+
+// Will be replaced by upstream withLink in Compose UI 1.7.0
+@OptIn(ExperimentalTextApi::class)
+inline fun <R : Any> AnnotatedString.Builder.withLink(
+ annotation: UrlAnnotation,
+ block: AnnotatedString.Builder.() -> R
+): R {
+ val index = pushUrlAnnotation(annotation)
+ return try {
+ block(this)
+ } finally {
+ pop(index)
+ }
+}
+
+private enum class PAGES(
+ val annotatedTopText: @Composable () -> AnnotatedString,
+ val image: Int,
+ val bottomText: Int
+) {
FIRST(
- R.string.auto_connect_carousel_first_slide_top_text,
+ annotatedTopText =
+ @Composable { buildTopText(id = R.string.auto_connect_carousel_first_slide_top_text) },
R.drawable.carousel_slide_1_cogwheel,
- R.string.auto_connect_carousel_first_slide_bottom_text
+ R.string.auto_connect_carousel_first_slide_bottom_text,
),
SECOND(
- R.string.auto_connect_carousel_second_slide_top_text,
+ annotatedTopText =
+ @Composable { buildTopText(id = R.string.auto_connect_carousel_second_slide_top_text) },
R.drawable.carousel_slide_2_always_on,
R.string.auto_connect_carousel_second_slide_bottom_text
),
THIRD(
- R.string.auto_connect_carousel_third_slide_top_text,
+ annotatedTopText = @Composable { buildLockdownTopText() },
R.drawable.carousel_slide_3_block_connections,
- R.string.auto_connect_carousel_third_slide_bottom_text
+ R.string.auto_connect_carousel_third_slide_bottom_text,
)
}
diff --git a/android/lib/resource/src/main/res/values/strings.xml b/android/lib/resource/src/main/res/values/strings.xml
index 67d5707631..3b60f3a4aa 100644
--- a/android/lib/resource/src/main/res/values/strings.xml
+++ b/android/lib/resource/src/main/res/values/strings.xml
@@ -87,10 +87,11 @@
<![CDATA[2. To enable Auto-connect, click on the toggle next to <b>Always-on VPN</b>.]]>
</string>
<string name="auto_connect_carousel_third_slide_top_text">
- <![CDATA[The Lockdown mode blocks all internet access if the VPN tunnel is manually disconnected. <br/><b>Warning: This setting blocks split apps and the Local Network Sharing feature</b>.]]>
+ <![CDATA[The Lockdown mode is called <b>Block connections without VPN</b> in the Android system settings. It helps minimize leaks, however it has some known limitations which you can read more about it]]>
</string>
+ <string name="auto_connect_carousel_third_slide_top_text_website_link">here</string>
<string name="auto_connect_carousel_third_slide_bottom_text">
- <![CDATA[3. To enable Lockdown mode, click on the toggle next to <b>Block connections without VPN</b>.]]>
+ <![CDATA[3. To enable Lockdown mode, click on the toggle next to <b>Block connections without VPN</b>.<br /><br /><b>Warning: This setting blocks split apps and the Local Network Sharing feature.</b>]]>
</string>
<string name="auto_connect_footer">Automatically connect to a server when the app launches.</string>
<string name="wireguard_mtu">WireGuard MTU</string>
diff --git a/android/lib/resource/src/main/res/values/strings_non_translatable.xml b/android/lib/resource/src/main/res/values/strings_non_translatable.xml
index 23a4d7c23f..c364ebff42 100644
--- a/android/lib/resource/src/main/res/values/strings_non_translatable.xml
+++ b/android/lib/resource/src/main/res/values/strings_non_translatable.xml
@@ -6,6 +6,8 @@
<string name="download_url" translatable="false">https://mullvad.net/download/vpn/android</string>
<string name="faqs_and_guides_url" translatable="false">https://mullvad.net/help/tag/mullvad-app/</string>
<string name="privacy_policy_url" translatable="false">https://mullvad.net/help/privacy-policy/</string>
+ <!-- TODO: Replace with https://mullvad.net/l/android-lockdown -->
+ <string name="lockdown_url" translatable="false">https://mullvad.net/help/using-mullvad-vpn-on-android</string>
<string name="split_tunneling" translatable="false">Split tunneling</string>
<string name="wireguard" translatable="false">WireGuard</string>
<string name="local_network_sharing_ip_ranges">