Skip to content

Commit

Permalink
Clarify lockdown limitations in guide
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-mullvad committed Jun 13, 2024
1 parent fbc385f commit 19f30ba
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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,
)
}
5 changes: 3 additions & 2 deletions android/lib/resource/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down

0 comments on commit 19f30ba

Please sign in to comment.