summaryrefslogtreecommitdiffhomepage
path: root/android/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'android/app/src')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt3
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/MullvadExposedDropdownMenuBox.kt7
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AutoConnectAndLockdownModeScreen.kt37
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt80
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt13
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/ApiAccessMethodTextField.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomTextField.kt4
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt4
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/ApiAccessRepositoryTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/RelayListFilterRepositoryTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt2
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt2
21 files changed, 79 insertions, 99 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt
index 2240e10eb8..0fd7674c89 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/NavigationComposeCell.kt
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.OpenInNew
@@ -116,7 +117,7 @@ internal fun NavigationTitleView(
@Composable
internal fun DefaultExternalLinkView(chevronContentDescription: String, tint: Color) {
Icon(
- imageVector = Icons.Default.OpenInNew,
+ imageVector = Icons.AutoMirrored.Default.OpenInNew,
contentDescription = chevronContentDescription,
tint = tint,
)
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/MullvadExposedDropdownMenuBox.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/MullvadExposedDropdownMenuBox.kt
index c558c078e1..82f9e49220 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/MullvadExposedDropdownMenuBox.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/MullvadExposedDropdownMenuBox.kt
@@ -9,6 +9,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldColors
@@ -20,10 +21,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import net.mullvad.mullvadvpn.lib.theme.color.menuItemColors
-/*
- This has bug with dropdown menu width that might be fixed in compose material 3 1.3
- https://issuetracker.google.com/issues/205589613
-*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MullvadExposedDropdownMenuBox(
@@ -40,7 +37,7 @@ fun MullvadExposedDropdownMenuBox(
modifier = modifier.clickable { expanded = !expanded },
) {
TextField(
- modifier = Modifier.fillMaxWidth().menuAnchor(),
+ modifier = Modifier.fillMaxWidth().menuAnchor(MenuAnchorType.PrimaryNotEditable, true),
readOnly = true,
value = title,
onValueChange = { /* Do nothing */ },
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 0a69b4fbb6..cafb9ef11d 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,8 +1,6 @@
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
import androidx.compose.foundation.layout.Arrangement
@@ -18,7 +16,6 @@ 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.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
@@ -37,12 +34,12 @@ 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.LinkAnnotation
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.withLink
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.constraintlayout.compose.ConstrainedLayoutReference
@@ -59,7 +56,6 @@ 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
@@ -80,7 +76,6 @@ fun AutoConnectAndLockdownMode(navigator: DestinationsNavigator) {
AutoConnectAndLockdownModeScreen(onBackClick = dropUnlessResumed { navigator.navigateUp() })
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) {
val context = LocalContext.current
@@ -101,7 +96,6 @@ fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) {
backButtonRef = backButtonRef,
nextButtonRef = nextButtonRef,
pager = pager,
- onOpenUrl = { url -> context.openLink(Uri.parse(url)) },
)
// Go to previous page
@@ -149,14 +143,12 @@ fun AutoConnectAndLockdownModeScreen(onBackClick: () -> Unit = {}) {
)
}
-@OptIn(ExperimentalFoundationApi::class, ExperimentalTextApi::class)
@Composable
private fun ConstraintLayoutScope.AutoConnectCarousel(
pagerState: PagerState,
backButtonRef: ConstrainedLayoutReference,
nextButtonRef: ConstrainedLayoutReference,
pager: ConstrainedLayoutReference,
- onOpenUrl: (String) -> Unit,
) {
HorizontalPager(
state = pagerState,
@@ -175,18 +167,13 @@ private fun ConstraintLayoutScope.AutoConnectCarousel(
modifier = Modifier.fillMaxWidth(),
) {
val annotatedTopText = page.annotatedTopText()
- ClickableText(
+ Text(
modifier = Modifier.padding(horizontal = Dimens.largePadding),
style =
MaterialTheme.typography.titleMedium.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant
),
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),
@@ -230,7 +217,6 @@ private fun CarouselNavigationButton(
}
}
-@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ConstraintLayoutScope.PageIndicator(
pagerState: PagerState,
@@ -279,14 +265,13 @@ private fun buildTopText(@StringRes id: Int) = buildAnnotatedString {
}
}
-@OptIn(ExperimentalTextApi::class)
@Composable
private fun buildLockdownTopText() = buildAnnotatedString {
append(buildTopText(id = R.string.auto_connect_carousel_third_slide_top_text))
append(" ")
withLink(
- UrlAnnotation(
+ LinkAnnotation.Url(
stringResource(id = R.string.lockdown_url).appendHideNavOnPlayBuild(IS_PLAY_BUILD)
)
) {
@@ -307,20 +292,6 @@ private fun buildLockdownTopText() = buildAnnotatedString {
}
}
-// 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,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
index f894eb8eba..69bc87ce9f 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/PrivacyDisclaimerScreen.kt
@@ -1,7 +1,5 @@
package net.mullvad.mullvadvpn.compose.screen
-import android.content.Context
-import android.net.Uri
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -11,9 +9,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.OpenInNew
@@ -27,10 +24,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
-import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.LinkAnnotation
+import androidx.compose.ui.text.SpanStyle
+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.withLink
+import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -52,7 +52,6 @@ import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.compose.util.toDp
import net.mullvad.mullvadvpn.constant.DAEMON_READY_TIMEOUT_MS
-import net.mullvad.mullvadvpn.lib.common.util.openLink
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.lib.theme.color.AlphaScrollbar
@@ -70,7 +69,6 @@ private fun PreviewPrivacyDisclaimerScreen() {
PrivacyDisclaimerScreen(
PrivacyDisclaimerViewState(isStartingService = false, isPlayBuild = false),
{},
- {},
)
}
}
@@ -108,19 +106,11 @@ fun PrivacyDisclaimer(navigator: DestinationsNavigator) {
}
}
}
- PrivacyDisclaimerScreen(
- state,
- { openPrivacyPolicy(context, state.isPlayBuild) },
- viewModel::setPrivacyDisclosureAccepted,
- )
+ PrivacyDisclaimerScreen(state, viewModel::setPrivacyDisclosureAccepted)
}
@Composable
-fun PrivacyDisclaimerScreen(
- state: PrivacyDisclaimerViewState,
- onPrivacyPolicyLinkClicked: () -> Unit,
- onAcceptClicked: () -> Unit,
-) {
+fun PrivacyDisclaimerScreen(state: PrivacyDisclaimerViewState, onAcceptClicked: () -> Unit) {
val topColor = MaterialTheme.colorScheme.primary
ScaffoldWithTopBar(topBarColor = topColor, onAccountClicked = null, onSettingsClicked = null) {
val scrollState = rememberScrollState()
@@ -136,7 +126,7 @@ fun PrivacyDisclaimerScreen(
),
verticalArrangement = Arrangement.SpaceBetween,
) {
- Content(onPrivacyPolicyLinkClicked)
+ Content(state.isPlayBuild)
ButtonPanel(state.isStartingService, onAcceptClicked)
}
@@ -144,7 +134,7 @@ fun PrivacyDisclaimerScreen(
}
@Composable
-private fun Content(onPrivacyPolicyLinkClicked: () -> Unit) {
+private fun Content(isPlayBuild: Boolean) {
Column {
Text(
text = stringResource(id = R.string.privacy_disclaimer_title),
@@ -169,26 +159,19 @@ private fun Content(onPrivacyPolicyLinkClicked: () -> Unit) {
color = MaterialTheme.colorScheme.onSurface,
)
- Row(modifier = Modifier.padding(top = 10.dp)) {
- ClickableText(
- text = AnnotatedString(stringResource(id = R.string.privacy_policy_label)),
- onClick = { onPrivacyPolicyLinkClicked() },
- style =
- TextStyle(
- fontSize = 12.sp,
- color = MaterialTheme.colorScheme.onSurface,
- textDecoration = TextDecoration.Underline,
- ),
+ Spacer(modifier = Modifier.height(fontSize.toDp() + Dimens.smallPadding))
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = buildPrivacyPolicyAnnotatedString(isPlayBuild),
+ modifier = Modifier.padding(end = Dimens.miniPadding),
)
Icon(
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
contentDescription = null,
modifier =
- Modifier.align(Alignment.CenterVertically)
- .padding(start = 2.dp, top = 2.dp)
- .width(10.dp)
- .height(10.dp),
+ Modifier.align(Alignment.CenterVertically).size(Dimens.privacyPolicyIconSize),
tint = MaterialTheme.colorScheme.onSurface,
)
}
@@ -196,6 +179,25 @@ private fun Content(onPrivacyPolicyLinkClicked: () -> Unit) {
}
@Composable
+private fun buildPrivacyPolicyAnnotatedString(isPlayBuild: Boolean) = buildAnnotatedString {
+ withLink(
+ LinkAnnotation.Url(
+ stringResource(R.string.privacy_policy_url).appendHideNavOnPlayBuild(isPlayBuild)
+ )
+ ) {
+ withStyle(
+ style =
+ SpanStyle(
+ color = MaterialTheme.colorScheme.onSurface,
+ textDecoration = TextDecoration.Underline,
+ )
+ ) {
+ append(stringResource(id = R.string.privacy_policy_label))
+ }
+ }
+}
+
+@Composable
private fun ButtonPanel(isStartingService: Boolean, onAcceptClicked: () -> Unit) {
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
if (isStartingService) {
@@ -208,13 +210,3 @@ private fun ButtonPanel(isStartingService: Boolean, onAcceptClicked: () -> Unit)
}
}
}
-
-private fun openPrivacyPolicy(context: Context, isPlayBuild: Boolean) {
- context.openLink(
- Uri.parse(
- context.resources
- .getString(R.string.privacy_policy_url)
- .appendHideNavOnPlayBuild(isPlayBuild)
- )
- )
-}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
index 660beaae94..0dddd926fc 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SplitTunnelingScreen.kt
@@ -1,7 +1,6 @@
package net.mullvad.mullvadvpn.compose.screen
import android.graphics.Bitmap
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -203,7 +202,6 @@ private fun LazyListScope.appList(
)
}
-@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.appItems(
apps: List<AppData>,
focusManager: FocusManager,
@@ -223,7 +221,7 @@ private fun LazyListScope.appItems(
isSelected = excluded,
enabled = enabled,
modifier =
- Modifier.animateItemPlacement()
+ Modifier.animateItem()
.fillMaxWidth()
.alpha(
if (enabled) {
@@ -247,12 +245,11 @@ private fun LazyListScope.appItems(
}
}
-@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean) {
itemWithDivider(key = key, contentType = ContentType.HEADER) {
HeaderCell(
modifier =
- Modifier.animateItemPlacement()
+ Modifier.animateItem()
.alpha(
if (enabled) {
AlphaVisible
@@ -266,7 +263,6 @@ private fun LazyListScope.headerItem(key: String, textId: Int, enabled: Boolean)
}
}
-@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.systemAppsToggle(
showSystemApps: Boolean,
onShowSystemAppsClick: (show: Boolean) -> Unit,
@@ -282,7 +278,7 @@ private fun LazyListScope.systemAppsToggle(
onCellClicked = { newValue -> onShowSystemAppsClick(newValue) },
isEnabled = enabled,
modifier =
- Modifier.animateItemPlacement()
+ Modifier.animateItem()
.alpha(
if (enabled) {
AlphaVisible
@@ -294,9 +290,8 @@ private fun LazyListScope.systemAppsToggle(
}
}
-@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.spacer() {
item(contentType = ContentType.SPACER) {
- Spacer(modifier = Modifier.animateItemPlacement().height(Dimens.mediumPadding))
+ Spacer(modifier = Modifier.animateItem().height(Dimens.mediumPadding))
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
index dd1ed8ffba..24b19cfae6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/VpnSettingsScreen.kt
@@ -20,7 +20,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -30,6 +29,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
@@ -469,7 +469,7 @@ fun VpnSettingsScreen(
isUnreachableLocalDnsWarningVisible =
item.isLocal && !state.isLocalNetworkSharingEnabled,
onClick = { navigateToDns(index, item.address) },
- modifier = Modifier.animateItemPlacement(),
+ modifier = Modifier.animateItem(),
)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/ApiAccessMethodTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/ApiAccessMethodTextField.kt
index 05ec572194..561465973e 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/ApiAccessMethodTextField.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/ApiAccessMethodTextField.kt
@@ -57,7 +57,7 @@ fun ApiAccessMethodTextField(
keyboardOptions =
KeyboardOptions(
capitalization = capitalization,
- autoCorrect = false,
+ autoCorrectEnabled = false,
keyboardType = keyboardType,
imeAction = imeAction,
),
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomTextField.kt
index d148550538..b64de576ee 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomTextField.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/CustomTextField.kt
@@ -47,10 +47,10 @@ fun CustomTextField(
capitalization: KeyboardCapitalization = KeyboardCapitalization.None,
keyboardOptions: KeyboardOptions =
KeyboardOptions(
+ capitalization = capitalization,
+ autoCorrectEnabled = false,
keyboardType = keyboardType,
imeAction = ImeAction.Done,
- autoCorrect = false,
- capitalization = capitalization,
),
) {
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 61d491094a..48dada2d61 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
@@ -93,7 +93,7 @@ import net.mullvad.mullvadvpn.viewmodel.WireguardCustomPortDialogViewModel
import org.apache.commons.validator.routines.InetAddressValidator
import org.koin.android.ext.koin.androidApplication
import org.koin.android.ext.koin.androidContext
-import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.module.dsl.viewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
index 209c213feb..8fd97dc63d 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCase.kt
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.usecase
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -53,6 +54,7 @@ class OutOfTimeUseCase(
?.isCausedByExpiredAccount() ?: false
}
+ @OptIn(ExperimentalCoroutinesApi::class)
private fun pastAccountExpiry(): Flow<Boolean?> =
repository.accountData
.flatMapLatest {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
index c25e71ef9c..200502dee4 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/FlowUtils.kt
@@ -3,6 +3,7 @@
package net.mullvad.mullvadvpn.util
import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
inline fun <T1, T2, T3, T4, T5, T6, R> combine(
@@ -28,6 +29,7 @@ inline fun <T1, T2, T3, T4, T5, T6, R> combine(
}
}
+@OptIn(ExperimentalCoroutinesApi::class)
fun <T> Deferred<T>.getOrDefault(default: T) =
try {
getCompleted()
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
index 9a0d9cdeff..a196d4ae90 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplashViewModel.kt
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -32,7 +33,7 @@ class SplashViewModel(
splashCompleteRepository.onSplashCompleted()
}
- private val _uiState = MutableStateFlow<SplashScreenState>(SplashScreenState(false))
+ private val _uiState = MutableStateFlow(SplashScreenState(false))
val uiState: StateFlow<SplashScreenState> = _uiState
private suspend fun getStartDestination(): SplashUiSideEffect {
@@ -61,6 +62,7 @@ class SplashViewModel(
}
// We know the user is logged in, but we need to find out if their account has expired
+ @OptIn(ExperimentalCoroutinesApi::class)
private suspend fun getLoggedInStartDestination(): SplashUiSideEffect {
val expiry = viewModelScope.async { accountRepository.accountData.filterNotNull().first() }
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
index 3bbdcea9cc..84ec047f06 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/InAppNotificationControllerTest.kt
@@ -7,6 +7,7 @@ import io.mockk.mockk
import io.mockk.unmockkAll
import kotlin.test.assertEquals
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -25,6 +26,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+@ExperimentalCoroutinesApi
@ExtendWith(TestCoroutineRule::class)
class InAppNotificationControllerTest {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/ApiAccessRepositoryTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/ApiAccessRepositoryTest.kt
index 9f0e9e9c68..4d5f305add 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/ApiAccessRepositoryTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/ApiAccessRepositoryTest.kt
@@ -6,6 +6,7 @@ import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -27,6 +28,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+@ExperimentalCoroutinesApi
class ApiAccessRepositoryTest {
private val mockManagementService: ManagementService = mockk()
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt
index 9148d45be7..d86c82748d 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/CustomListsRepositoryTest.kt
@@ -7,6 +7,7 @@ import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -23,6 +24,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+@ExperimentalCoroutinesApi
class CustomListsRepositoryTest {
private val mockManagementService: ManagementService = mockk()
private lateinit var customListsRepository: CustomListsRepository
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/RelayListFilterRepositoryTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/RelayListFilterRepositoryTest.kt
index 53d2b1044d..6ec1ae1fea 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/RelayListFilterRepositoryTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/repository/RelayListFilterRepositoryTest.kt
@@ -7,6 +7,7 @@ import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -21,6 +22,7 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+@ExperimentalCoroutinesApi
class RelayListFilterRepositoryTest {
private val mockManagementService: ManagementService = mockk()
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
index 3e905d87f6..d8438cafe0 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
@@ -9,6 +9,7 @@ import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.consumeAsFlow
@@ -29,6 +30,7 @@ import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+@ExperimentalCoroutinesApi
class OutOfTimeUseCaseTest {
private val mockAccountRepository: AccountRepository = mockk()
private val mockConnectionProxy: ConnectionProxy = mockk()
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
index 4fb6047066..d0af241314 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModelTest.kt
@@ -10,6 +10,7 @@ import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.mockk.unmockkAll
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -24,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+@ExperimentalCoroutinesApi
@ExtendWith(TestCoroutineRule::class)
class DeviceRevokedViewModelTest {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
index a01554befc..ba636197f4 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
@@ -13,6 +13,7 @@ import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import kotlin.test.assertIs
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
@@ -34,6 +35,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+@ExperimentalCoroutinesApi
@ExtendWith(TestCoroutineRule::class)
class LoginViewModelTest {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt
index 00fd6e752c..63a0907629 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModelTest.kt
@@ -11,6 +11,7 @@ import io.mockk.unmockkAll
import io.mockk.verify
import java.util.concurrent.TimeUnit
import kotlin.test.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -27,6 +28,7 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Timeout
import org.junit.jupiter.api.extension.ExtendWith
+@ExperimentalCoroutinesApi
@ExtendWith(TestCoroutineRule::class)
@Timeout(3000L, unit = TimeUnit.MILLISECONDS)
class SplitTunnelingViewModelTest {
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
index 5b878b52b9..d04cdc5d26 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModelTest.kt
@@ -13,6 +13,7 @@ import io.mockk.unmockkAll
import io.mockk.verify
import kotlin.test.assertEquals
import kotlin.test.assertIs
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -38,6 +39,7 @@ import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+@ExperimentalCoroutinesApi
@ExtendWith(TestCoroutineRule::class)
class VpnSettingsViewModelTest {