diff options
| author | Albin <albin@mullvad.net> | 2023-03-22 09:07:53 +0100 |
|---|---|---|
| committer | Albin <albin@mullvad.net> | 2023-03-22 10:39:58 +0100 |
| commit | 68c356e5afa2165cadef86d43966e51d9203102c (patch) | |
| tree | 0a172dacad334066be40ea1239691ed963139dac | |
| parent | e5d106c9260e0f282c75d105da7b0247535cdfdd (diff) | |
| download | mullvadvpn-68c356e5afa2165cadef86d43966e51d9203102c.tar.xz mullvadvpn-68c356e5afa2165cadef86d43966e51d9203102c.zip | |
Apply project-wide `kotlinLangStyle` formatting
230 files changed, 3610 insertions, 4215 deletions
diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/RecyclerViewMatcher.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/RecyclerViewMatcher.kt index 2e87df9720..c5093f3ceb 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/RecyclerViewMatcher.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/RecyclerViewMatcher.kt @@ -19,27 +19,27 @@ class RecyclerViewMatcher(private val recyclerViewId: Int) { var childView: View? = null override fun describeTo(description: Description) { - val idDescription = resources?.let { - try { - it.getResourceName(recyclerViewId) - } catch (var4: NotFoundException) { - "$recyclerViewId (resource name not found)" + val idDescription = + resources?.let { + try { + it.getResourceName(recyclerViewId) + } catch (var4: NotFoundException) { + "$recyclerViewId (resource name not found)" + } } - } ?: recyclerViewId.toString() + ?: recyclerViewId.toString() description.appendText("with id: $idDescription") } override fun matchesSafely(view: View): Boolean { resources = view.resources - val recyclerView = - view.rootView.findViewById<View>(recyclerViewId) as RecyclerView? + val recyclerView = view.rootView.findViewById<View>(recyclerViewId) as RecyclerView? if (recyclerView == null || recyclerView.id != recyclerViewId) { return false } childView = recyclerView.findViewHolderForAdapterPosition(position)?.itemView - val targetView = targetViewId?.let { id -> - childView?.findViewById<View>(id) - } ?: childView + val targetView = + targetViewId?.let { id -> childView?.findViewById<View>(id) } ?: childView return view == targetView } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt index 4c94fced01..3fffcb63c1 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/ChangelogDialogTest.kt @@ -19,11 +19,9 @@ import org.junit.Rule import org.junit.Test class ChangelogDialogTest { - @get:Rule - val composeTestRule = createComposeRule() + @get:Rule val composeTestRule = createComposeRule() - @MockK - lateinit var mockedViewModel: ChangelogViewModel + @MockK lateinit var mockedViewModel: ChangelogViewModel @Before fun setup() { @@ -33,34 +31,25 @@ class ChangelogDialogTest { @Test fun testShowChangeLogWhenNeeded() { // Arrange - every { - mockedViewModel.changelogDialogUiState - } returns MutableStateFlow(ChangelogDialogUiState.Show(listOf(CHANGELOG_ITEM))) - every { - mockedViewModel.dismissChangelogDialog() - } just Runs + every { mockedViewModel.changelogDialogUiState } returns + MutableStateFlow(ChangelogDialogUiState.Show(listOf(CHANGELOG_ITEM))) + every { mockedViewModel.dismissChangelogDialog() } just Runs composeTestRule.setContent { AppTheme { ChangelogDialog( changesList = listOf(CHANGELOG_ITEM), version = CHANGELOG_VERSION, - onDismiss = { - mockedViewModel.dismissChangelogDialog() - } + onDismiss = { mockedViewModel.dismissChangelogDialog() } ) } } // Check changelog content showed within dialog - composeTestRule - .onNodeWithText(CHANGELOG_ITEM) - .assertExists() + composeTestRule.onNodeWithText(CHANGELOG_ITEM).assertExists() // perform click on Got It button to check if dismiss occur - composeTestRule - .onNodeWithText(CHANGELOG_BUTTON_TEXT) - .performClick() + composeTestRule.onNodeWithText(CHANGELOG_BUTTON_TEXT).performClick() // Assert verify { mockedViewModel.dismissChangelogDialog() } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt index 13d98455c5..741e57612e 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreenTest.kt @@ -18,11 +18,9 @@ import org.junit.Rule import org.junit.Test class DeviceRevokedScreenTest { - @get:Rule - val composeTestRule = createComposeRule() + @get:Rule val composeTestRule = createComposeRule() - @MockK - lateinit var mockedViewModel: DeviceRevokedViewModel + @MockK lateinit var mockedViewModel: DeviceRevokedViewModel @Before fun setup() { @@ -33,59 +31,35 @@ class DeviceRevokedScreenTest { @Test fun testUnblockWarningShowingWhenSecured() { // Arrange - every { - mockedViewModel.uiState - } returns MutableStateFlow(DeviceRevokedUiState.SECURED) + every { mockedViewModel.uiState } returns MutableStateFlow(DeviceRevokedUiState.SECURED) // Act - composeTestRule.setContent { - AppTheme { - DeviceRevokedScreen(mockedViewModel) - } - } + composeTestRule.setContent { AppTheme { DeviceRevokedScreen(mockedViewModel) } } // Assert - composeTestRule - .onNodeWithText(UNBLOCK_WARNING) - .assertExists() + composeTestRule.onNodeWithText(UNBLOCK_WARNING).assertExists() } @Test fun testUnblockWarningNotShowingWhenNotSecured() { // Arrange - every { - mockedViewModel.uiState - } returns MutableStateFlow(DeviceRevokedUiState.UNSECURED) + every { mockedViewModel.uiState } returns MutableStateFlow(DeviceRevokedUiState.UNSECURED) // Act - composeTestRule.setContent { - AppTheme { - DeviceRevokedScreen(mockedViewModel) - } - } + composeTestRule.setContent { AppTheme { DeviceRevokedScreen(mockedViewModel) } } // Assert - composeTestRule - .onNodeWithText(UNBLOCK_WARNING) - .assertDoesNotExist() + composeTestRule.onNodeWithText(UNBLOCK_WARNING).assertDoesNotExist() } @Test fun testGoToLogin() { // Arrange - every { - mockedViewModel.uiState - } returns MutableStateFlow(DeviceRevokedUiState.UNSECURED) - composeTestRule.setContent { - AppTheme { - DeviceRevokedScreen(mockedViewModel) - } - } + every { mockedViewModel.uiState } returns MutableStateFlow(DeviceRevokedUiState.UNSECURED) + composeTestRule.setContent { AppTheme { DeviceRevokedScreen(mockedViewModel) } } // Act - composeTestRule - .onNodeWithText(GO_TO_LOGIN_BUTTON_TEXT) - .performClick() + composeTestRule.onNodeWithText(GO_TO_LOGIN_BUTTON_TEXT).performClick() // Assert verify { mockedViewModel.onGoToLoginClicked() } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlowTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlowTest.kt index 709f330b0d..1dcb4dff5b 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlowTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlowTest.kt @@ -15,9 +15,7 @@ class HandlerFlowTest { val looper by lazy { Looper.getMainLooper() } val handler: HandlerFlow<Data?> by lazy { - HandlerFlow(looper) { message -> - message.data.getParcelable(DATA_KEY) - } + HandlerFlow(looper) { message -> message.data.getParcelable(DATA_KEY) } } @Test @@ -32,9 +30,8 @@ class HandlerFlowTest { } private fun sendMessage(messageData: Data) { - val message = Message().apply { - data = Bundle().apply { putParcelable(DATA_KEY, messageData) } - } + val message = + Message().apply { data = Bundle().apply { putParcelable(DATA_KEY, messageData) } } handler.handleMessage(message) } @@ -42,7 +39,6 @@ class HandlerFlowTest { companion object { const val DATA_KEY = "data" - @Parcelize - data class Data(val id: Int) : Parcelable + @Parcelize data class Data(val id: Int) : Parcelable } } diff --git a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt index 0a48801f81..f0b6d0a24d 100644 --- a/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt +++ b/android/app/src/androidTest/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragmentTest.kt @@ -62,12 +62,13 @@ class SplitTunnelingFragmentTest : KoinTest { } @get:Rule - val mockProvider = MockProviderRule.create { clazz -> - when (clazz) { - SplitTunnelingViewModel::class -> mockedViewModel - else -> mockkClass(clazz) + val mockProvider = + MockProviderRule.create { clazz -> + when (clazz) { + SplitTunnelingViewModel::class -> mockedViewModel + else -> mockkClass(clazz) + } } - } @Before fun setUp() { @@ -95,21 +96,22 @@ class SplitTunnelingFragmentTest : KoinTest { @Test fun test_fragment_loading() { - val scenario = launchFragmentInContainer<SplitTunnelingFragment>( - themeResId = R.style.AppTheme, initialState = Lifecycle.State.CREATED - ) + val scenario = + launchFragmentInContainer<SplitTunnelingFragment>( + themeResId = R.style.AppTheme, + initialState = Lifecycle.State.CREATED + ) scenario.moveToState(Lifecycle.State.RESUMED) sharedFlow.tryEmit(emptyList()) - verifyAll { - mockedViewModel.listItems - } + verifyAll { mockedViewModel.listItems } } @Test fun test_fragment_list_displayed() = runBlocking { launchFragmentInContainer<SplitTunnelingFragment>( - themeResId = R.style.AppTheme, initialState = Lifecycle.State.RESUMED + themeResId = R.style.AppTheme, + initialState = Lifecycle.State.RESUMED ) sharedFlow.emit( @@ -125,26 +127,26 @@ class SplitTunnelingFragmentTest : KoinTest { onView(withRecyclerView(R.id.recyclerView).atPositionOnView(0, R.id.plain_text)) .check(matches(withText("Test Item"))) - verifyAll { - mockedViewModel.listItems - } + verifyAll { mockedViewModel.listItems } } @Test fun test_fragment_list_click_application_item() = runBlocking { - val testListItem = ListItemData.build("test.package.name") { - type = ListItemData.APPLICATION - text = "Test App Name" - action = ListItemData.ItemAction("test.package.name") - widget = WidgetState.ImageState(R.drawable.ic_icons_add) - } + val testListItem = + ListItemData.build("test.package.name") { + type = ListItemData.APPLICATION + text = "Test App Name" + action = ListItemData.ItemAction("test.package.name") + widget = WidgetState.ImageState(R.drawable.ic_icons_add) + } coEvery { mockedViewModel.processIntent(ViewIntent.ChangeApplicationGroup(testListItem)) } just Runs launchFragmentInContainer<SplitTunnelingFragment>( - themeResId = R.style.AppTheme, initialState = Lifecycle.State.RESUMED + themeResId = R.style.AppTheme, + initialState = Lifecycle.State.RESUMED ) sharedFlow.emit(listOf(testListItem)) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt index ae00e9e16d..4b34886c34 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/MullvadApplication.kt @@ -12,8 +12,6 @@ class MullvadApplication : Application() { override fun onCreate() { super.onCreate() // Used to create/start separate DI graphs for each process. Avoid non-common classes etc. - startKoin { - androidContext(this@MullvadApplication) - } + startKoin { androidContext(this@MullvadApplication) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProvider.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProvider.kt index 774392f1a6..1220e71f07 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProvider.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProvider.kt @@ -9,12 +9,12 @@ class ApplicationsProvider( private val thisPackageName: String ) { private val applicationFilterPredicate: (ApplicationInfo) -> Boolean = { appInfo -> - hasInternetPermission(appInfo.packageName) && - !isSelfApplication(appInfo.packageName) + hasInternetPermission(appInfo.packageName) && !isSelfApplication(appInfo.packageName) } fun getAppsList(): List<AppData> { - return packageManager.getInstalledApplications(PackageManager.GET_META_DATA) + return packageManager + .getInstalledApplications(PackageManager.GET_META_DATA) .asSequence() .filter(applicationFilterPredicate) .map { info -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt index cd63483d45..e07a34bef1 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/BaseCell.kt @@ -37,48 +37,36 @@ fun BaseCell( val cellVerticalSpacing = dimensionResource(id = R.dimen.cell_label_vertical_padding) val subtitleVerticalSpacing = dimensionResource(id = R.dimen.cell_footer_top_padding) - Column( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .background(background) - ) { - + Column(modifier = Modifier.fillMaxWidth().wrapContentHeight().background(background)) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start, - modifier = Modifier - .height(cellHeight) - .fillMaxWidth() - .clickable { onCellClicked.invoke() } - .padding(start = startPadding, end = endPadding) - + modifier = + Modifier.height(cellHeight) + .fillMaxWidth() + .clickable { onCellClicked.invoke() } + .padding(start = startPadding, end = endPadding) ) { title() Spacer(modifier = Modifier.weight(1.0f)) - Column( - modifier = modifier - .wrapContentWidth() - .wrapContentHeight() - ) { - bodyView() - } + Column(modifier = modifier.wrapContentWidth().wrapContentHeight()) { bodyView() } } if (subtitle != null) { Row( - modifier = subtitleModifier - .background(MullvadDarkBlue) - .padding( - start = startPadding, - top = subtitleVerticalSpacing, - end = endPadding, - bottom = cellVerticalSpacing - ) - .fillMaxWidth() - .wrapContentHeight() + modifier = + subtitleModifier + .background(MullvadDarkBlue) + .padding( + start = startPadding, + top = subtitleVerticalSpacing, + end = endPadding, + bottom = cellVerticalSpacing + ) + .fillMaxWidth() + .wrapContentHeight() ) { subtitle() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomDnsComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomDnsComposeCell.kt index d5fa79fe09..3ed13f812f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomDnsComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/CustomDnsComposeCell.kt @@ -21,17 +21,11 @@ import net.mullvad.mullvadvpn.compose.theme.MullvadWhite60 @Preview @Composable private fun PreviewDnsComposeCell() { - CustomDnsComposeCell( - checkboxDefaultState = true, - onToggle = {} - ) + CustomDnsComposeCell(checkboxDefaultState = true, onToggle = {}) } @Composable -fun CustomDnsComposeCell( - checkboxDefaultState: Boolean, - onToggle: (Boolean) -> Unit -) { +fun CustomDnsComposeCell(checkboxDefaultState: Boolean, onToggle: (Boolean) -> Unit) { val titleModifier = Modifier val bodyViewModifier = Modifier val subtitleModifier = Modifier @@ -40,9 +34,7 @@ fun CustomDnsComposeCell( title = { CustomDnsCellTitle(modifier = titleModifier) }, bodyView = { CustomDnsCellView( - switchTriggered = { - onToggle(it) - }, + switchTriggered = { onToggle(it) }, isToggled = checkboxDefaultState, modifier = bodyViewModifier ) @@ -53,9 +45,7 @@ fun CustomDnsComposeCell( } @Composable -fun CustomDnsCellTitle( - modifier: Modifier -) { +fun CustomDnsCellTitle(modifier: Modifier) { val textSize = dimensionResource(id = R.dimen.text_medium_plus).value.sp Text( text = stringResource(R.string.enable_custom_dns), @@ -63,27 +53,14 @@ fun CustomDnsCellTitle( fontWeight = FontWeight.Bold, fontSize = textSize, color = MullvadWhite, - modifier = modifier - .wrapContentWidth(align = Alignment.End) - .wrapContentHeight() + modifier = modifier.wrapContentWidth(align = Alignment.End).wrapContentHeight() ) } @Composable -fun CustomDnsCellView( - switchTriggered: (Boolean) -> Unit, - isToggled: Boolean, - modifier: Modifier -) { - Row( - modifier = modifier - .wrapContentWidth() - .wrapContentHeight() - ) { - CellSwitch( - isChecked = isToggled, - onCheckedChange = null - ) +fun CustomDnsCellView(switchTriggered: (Boolean) -> Unit, isToggled: Boolean, modifier: Modifier) { + Row(modifier = modifier.wrapContentWidth().wrapContentHeight()) { + CellSwitch(isChecked = isToggled, onCheckedChange = null) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt index 238ecb8d8e..603c2f60df 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/DnsCell.kt @@ -23,11 +23,7 @@ import net.mullvad.mullvadvpn.compose.theme.MullvadHelmetYellow @Preview @Composable private fun PreviewDnsCell() { - DnsCell( - address = "0.0.0.0", - isUnreachableLocalDnsWarningVisible = true, - onClick = {} - ) + DnsCell(address = "0.0.0.0", isUnreachableLocalDnsWarningVisible = true, onClick = {}) } @Composable @@ -41,12 +37,7 @@ fun DnsCell( val startPadding = 54.dp BaseCell( - title = { - DnsTitle( - address = address, - modifier = titleModifier - ) - }, + title = { DnsTitle(address = address, modifier = titleModifier) }, bodyView = { if (isUnreachableLocalDnsWarningVisible) { Icon( @@ -64,10 +55,7 @@ fun DnsCell( } @Composable -private fun DnsTitle( - address: String, - modifier: Modifier = Modifier -) { +private fun DnsTitle(address: String, modifier: Modifier = Modifier) { val textSize = dimensionResource(id = R.dimen.text_medium).value.sp Text( text = address, @@ -75,8 +63,6 @@ private fun DnsTitle( fontSize = textSize, fontStyle = FontStyle.Normal, textAlign = TextAlign.Start, - modifier = modifier - .wrapContentWidth(align = Alignment.End) - .wrapContentHeight() + modifier = modifier.wrapContentWidth(align = Alignment.End).wrapContentHeight() ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt index 5be7f04d3b..2bc4679710 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/cell/MtuComposeCell.kt @@ -22,10 +22,7 @@ import net.mullvad.mullvadvpn.constant.MTU_MIN_VALUE @Preview @Composable fun MtuComposeCellPreview() { - MtuComposeCell( - mtuValue = "1300", - onEditMtu = {} - ) + MtuComposeCell(mtuValue = "1300", onEditMtu = {}) } @Composable @@ -38,24 +35,15 @@ fun MtuComposeCell( BaseCell( title = { MtuTitle(modifier = titleModifier) }, - bodyView = { - MtuBodyView( - mtuValue = mtuValue, - modifier = titleModifier - ) - }, + bodyView = { MtuBodyView(mtuValue = mtuValue, modifier = titleModifier) }, subtitle = { MtuSubtitle(subtitleModifier) }, subtitleModifier = subtitleModifier, - onCellClicked = { - onEditMtu.invoke() - } + onCellClicked = { onEditMtu.invoke() } ) } @Composable -private fun MtuTitle( - modifier: Modifier -) { +private fun MtuTitle(modifier: Modifier) { val textSize = dimensionResource(id = R.dimen.text_medium_plus).value.sp Text( text = stringResource(R.string.wireguard_mtu), @@ -63,22 +51,13 @@ private fun MtuTitle( fontWeight = FontWeight.Bold, fontSize = textSize, color = Color.White, - modifier = modifier - .wrapContentWidth1(align = Alignment.End) - .wrapContentHeight() + modifier = modifier.wrapContentWidth1(align = Alignment.End).wrapContentHeight() ) } @Composable -private fun MtuBodyView( - mtuValue: String, - modifier: Modifier -) { - Row( - modifier = modifier - .wrapContentWidth1() - .wrapContentHeight() - ) { +private fun MtuBodyView(mtuValue: String, modifier: Modifier) { + Row(modifier = modifier.wrapContentWidth1().wrapContentHeight()) { Text( text = mtuValue.ifEmpty { stringResource(id = R.string.hint_default) }, color = Color.White 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 b4e05ebbd3..87ef0cbfe5 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 @@ -19,36 +19,26 @@ import net.mullvad.mullvadvpn.R @Preview @Composable private fun PreviewNavigationCell() { - NavigationComposeCell( - title = "Navigation sample", - onClick = {} - ) + NavigationComposeCell(title = "Navigation sample", onClick = {}) } @Composable fun NavigationComposeCell( title: String, modifier: Modifier = Modifier, - bodyView: @Composable () -> Unit = { - DefaultNavigationView(chevronContentDescription = title) - }, + bodyView: @Composable () -> Unit = { DefaultNavigationView(chevronContentDescription = title) }, onClick: () -> Unit ) { BaseCell( onCellClicked = onClick, title = { NavigationTitleView(title = title, modifier = modifier) }, - bodyView = { - bodyView() - }, + bodyView = { bodyView() }, subtitle = null, ) } @Composable -private fun NavigationTitleView( - title: String, - modifier: Modifier = Modifier -) { +private fun NavigationTitleView(title: String, modifier: Modifier = Modifier) { val textMediumSize = dimensionResource(id = R.dimen.text_medium_plus).value.sp Text( text = title, @@ -56,9 +46,7 @@ private fun NavigationTitleView( fontWeight = FontWeight.Bold, fontSize = textMediumSize, color = Color.White, - modifier = modifier - .wrapContentWidth(align = Alignment.End) - .wrapContentHeight() + modifier = modifier.wrapContentWidth(align = Alignment.End).wrapContentHeight() ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt index dc2068c5e5..f8c7127d1a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Button.kt @@ -29,13 +29,14 @@ fun ActionButton( enabled = isEnabled, // Required along with defaultMinSize to control size and padding. contentPadding = PaddingValues(0.dp), - modifier = modifier - .height(dimensionResource(id = R.dimen.button_height)) - .defaultMinSize( - minWidth = 0.dp, - minHeight = dimensionResource(id = R.dimen.button_height) - ) - .fillMaxWidth(), + modifier = + modifier + .height(dimensionResource(id = R.dimen.button_height)) + .defaultMinSize( + minWidth = 0.dp, + minHeight = dimensionResource(id = R.dimen.button_height) + ) + .fillMaxWidth(), colors = colors ) { Text( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt index b117f7cbf3..570bdf4eb4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/CollapsingTopBar.kt @@ -63,34 +63,24 @@ fun CollapsingTopBar( val minTitleSize = 20 Spacer( - modifier = Modifier - .fillMaxWidth() - .height(expandedToolbarHeight) - .background(backgroundColor) + modifier = Modifier.fillMaxWidth().height(expandedToolbarHeight).background(backgroundColor) ) Button( - modifier = Modifier - .wrapContentWidth() - .wrapContentHeight(), + modifier = Modifier.wrapContentWidth().wrapContentHeight(), onClick = onBackClicked, - colors = ButtonDefaults.buttonColors( - contentColor = Color.White, - backgroundColor = MullvadDarkBlue - ) + colors = + ButtonDefaults.buttonColors( + contentColor = Color.White, + backgroundColor = MullvadDarkBlue + ) ) { Image( painter = painterResource(id = R.drawable.icon_back), contentDescription = stringResource(id = R.string.back), - modifier = Modifier - .width(iconSize) - .height(iconSize) - ) - Spacer( - modifier = Modifier - .width(iconPadding) - .fillMaxHeight() + modifier = Modifier.width(iconSize).height(iconSize) ) + Spacer(modifier = Modifier.width(iconPadding).fillMaxHeight()) Text( text = backTitle, color = MullvadWhite60, @@ -101,23 +91,22 @@ fun CollapsingTopBar( Text( text = title, - style = TextStyle( - color = Color.White, - fontWeight = FontWeight.Bold, - textAlign = TextAlign.End - ), - modifier = modifier - .padding( + style = + TextStyle(color = Color.White, fontWeight = FontWeight.Bold, textAlign = TextAlign.End), + modifier = + modifier.padding( start = sideMargin, end = sideMargin, top = (minTopPadding + (maxTopPadding - minTopPadding) * progress).dp, bottom = verticalMargin ), - fontSize = topBarSize( - progress = progress, - minTitleSize = minTitleSize, - maxTitleSize = maxTitleSize - ).sp + fontSize = + topBarSize( + progress = progress, + minTitleSize = minTitleSize, + maxTitleSize = maxTitleSize + ) + .sp ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/HtmlText.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/HtmlText.kt index 545d724228..b22390cfa8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/HtmlText.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/HtmlText.kt @@ -8,17 +8,11 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.core.text.HtmlCompat @Composable -fun HtmlText( - htmlFormattedString: String, - textSize: Float, - modifier: Modifier = Modifier -) { +fun HtmlText(htmlFormattedString: String, textSize: Float, modifier: Modifier = Modifier) { AndroidView( modifier = modifier, factory = { context -> - TextView(context).apply { - setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) - } + TextView(context).apply { setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize) } }, update = { it.text = HtmlCompat.fromHtml(htmlFormattedString, HtmlCompat.FROM_HTML_MODE_COMPACT) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt index bd7aa0926b..bc46f93e03 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/List.kt @@ -34,43 +34,28 @@ fun ListItem( val itemColor = colorResource(id = R.color.blue) Box( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 1.dp) - .height(50.dp) - .background(itemColor), + modifier = + Modifier.fillMaxWidth().padding(vertical = 1.dp).height(50.dp).background(itemColor), ) { Text( text = text, fontSize = 18.sp, color = Color.White, - modifier = Modifier - .padding( - horizontal = 16.dp - ) - .align(Alignment.CenterStart) + modifier = Modifier.padding(horizontal = 16.dp).align(Alignment.CenterStart) ) - Box( - modifier = Modifier - .align(Alignment.CenterEnd) - .padding(horizontal = 12.dp) - ) { + Box(modifier = Modifier.align(Alignment.CenterEnd).padding(horizontal = 12.dp)) { if (isLoading) { CircularProgressIndicator( strokeWidth = 3.dp, color = Color.White, - modifier = Modifier - .height(24.dp) - .width(24.dp) + modifier = Modifier.height(24.dp).width(24.dp) ) } else if (iconResourceId != null) { Image( painter = painterResource(id = iconResourceId), contentDescription = "Remove", - modifier = Modifier - .align(Alignment.CenterEnd) - .clickable { onClick() } + modifier = Modifier.align(Alignment.CenterEnd).clickable { onClick() } ) } } @@ -78,42 +63,30 @@ fun ListItem( } @Composable -fun ChangeListItem( - text: String -) { +fun ChangeListItem(text: String) { ConstraintLayout { val (bullet, changeLog) = createRefs() val smallPadding = dimensionResource(id = R.dimen.small_padding) Box( - modifier = Modifier - .constrainAs(bullet) { + modifier = + Modifier.constrainAs(bullet) { top.linkTo(parent.top) start.linkTo(parent.absoluteLeft) } ) { - Text( - text = "•", - fontSize = 14.sp, - color = Color.White - ) + Text(text = "•", fontSize = 14.sp, color = Color.White) } Box( - modifier = Modifier - .absolutePadding(left = dimensionResource(id = R.dimen.medium_padding)) - .constrainAs(changeLog) { - top.linkTo(parent.top) - bottom.linkTo(parent.bottom, margin = smallPadding) - start.linkTo(parent.start) - end.linkTo(parent.end) - } + modifier = + Modifier.absolutePadding(left = dimensionResource(id = R.dimen.medium_padding)) + .constrainAs(changeLog) { + top.linkTo(parent.top) + bottom.linkTo(parent.bottom, margin = smallPadding) + start.linkTo(parent.start) + end.linkTo(parent.end) + } ) { - Text( - text = text, - fontSize = 14.sp, - color = Color.White, - modifier = Modifier - - ) + Text(text = text, fontSize = 14.sp, color = Color.White, modifier = Modifier) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt index c6d32a4df9..3d23071e94 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scaffolding.kt @@ -78,12 +78,12 @@ fun CollapsableAwareToolbarScaffold( BoxWithConstraints( modifier = Modifier.onGloballyPositioned { bodyHeight = it.size.height } ) { - val minMaxToolbarHeightDiff = with(state) { - toolbarState.maxHeight - toolbarState.minHeight - } - val isContentHigherThanCollapseThreshold = with(LocalDensity.current) { - bodyHeight > maxHeight.toPx() - minMaxToolbarHeightDiff - } + val minMaxToolbarHeightDiff = + with(state) { toolbarState.maxHeight - toolbarState.minHeight } + val isContentHigherThanCollapseThreshold = + with(LocalDensity.current) { + bodyHeight > maxHeight.toPx() - minMaxToolbarHeightDiff + } isCollapsable = isContentHigherThanCollapseThreshold body() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt index 1ca651a9a9..da5759ec1c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Scrollbar.kt @@ -82,19 +82,24 @@ private fun Modifier.drawScrollbar( state: ScrollState, orientation: Orientation, reverseScrolling: Boolean -): Modifier = drawScrollbar( - orientation, reverseScrolling -) { reverseDirection, atEnd, color, alpha -> - if (state.maxValue > 0) { - val canvasSize = if (orientation == Orientation.Horizontal) size.width else size.height - val totalSize = canvasSize + state.maxValue - val thumbSize = canvasSize / totalSize * canvasSize - val startOffset = state.value / totalSize * canvasSize - drawScrollbar( - orientation, reverseDirection, atEnd, color, alpha, thumbSize, startOffset - ) +): Modifier = + drawScrollbar(orientation, reverseScrolling) { reverseDirection, atEnd, color, alpha -> + if (state.maxValue > 0) { + val canvasSize = if (orientation == Orientation.Horizontal) size.width else size.height + val totalSize = canvasSize + state.maxValue + val thumbSize = canvasSize / totalSize * canvasSize + val startOffset = state.value / totalSize * canvasSize + drawScrollbar( + orientation, + reverseDirection, + atEnd, + color, + alpha, + thumbSize, + startOffset + ) + } } -} fun Modifier.drawHorizontalScrollbar( state: LazyListState, @@ -110,57 +115,73 @@ private fun Modifier.drawScrollbar( state: LazyListState, orientation: Orientation, reverseScrolling: Boolean -): Modifier = drawScrollbar( - orientation, reverseScrolling -) { reverseDirection, atEnd, color, alpha -> - val layoutInfo = state.layoutInfo - val viewportSize = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset - val items = layoutInfo.visibleItemsInfo - val itemsSize = items.fastSumBy { it.size } - if (items.size < layoutInfo.totalItemsCount || itemsSize > viewportSize) { - val estimatedItemSize = if (items.isEmpty()) 0f else itemsSize.toFloat() / items.size - val totalSize = estimatedItemSize * layoutInfo.totalItemsCount - val canvasSize = if (orientation == Orientation.Horizontal) size.width else size.height - val thumbSize = viewportSize / totalSize * canvasSize - val startOffset = if (items.isEmpty()) 0f else items.first().run { - (estimatedItemSize * index - offset) / totalSize * canvasSize +): Modifier = + drawScrollbar(orientation, reverseScrolling) { reverseDirection, atEnd, color, alpha -> + val layoutInfo = state.layoutInfo + val viewportSize = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset + val items = layoutInfo.visibleItemsInfo + val itemsSize = items.fastSumBy { it.size } + if (items.size < layoutInfo.totalItemsCount || itemsSize > viewportSize) { + val estimatedItemSize = if (items.isEmpty()) 0f else itemsSize.toFloat() / items.size + val totalSize = estimatedItemSize * layoutInfo.totalItemsCount + val canvasSize = if (orientation == Orientation.Horizontal) size.width else size.height + val thumbSize = viewportSize / totalSize * canvasSize + val startOffset = + if (items.isEmpty()) 0f + else + items.first().run { + (estimatedItemSize * index - offset) / totalSize * canvasSize + } + drawScrollbar( + orientation, + reverseDirection, + atEnd, + color, + alpha, + thumbSize, + startOffset + ) } - drawScrollbar( - orientation, reverseDirection, atEnd, color, alpha, thumbSize, startOffset - ) } -} fun Modifier.drawVerticalScrollbar( state: LazyGridState, spanCount: Int, reverseScrolling: Boolean = false -): Modifier = drawScrollbar( - Orientation.Vertical, reverseScrolling -) { reverseDirection, atEnd, color, alpha -> - val layoutInfo = state.layoutInfo - val viewportSize = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset - val items = layoutInfo.visibleItemsInfo - val rowCount = (items.size + spanCount - 1) / spanCount - var itemsSize = 0 - for (i in 0 until rowCount) { - itemsSize += items[i * spanCount].size.height - } - if (items.size < layoutInfo.totalItemsCount || itemsSize > viewportSize) { - val estimatedItemSize = if (rowCount == 0) 0f else itemsSize.toFloat() / rowCount - val totalRow = (layoutInfo.totalItemsCount + spanCount - 1) / spanCount - val totalSize = estimatedItemSize * totalRow - val canvasSize = size.height - val thumbSize = viewportSize / totalSize * canvasSize - val startOffset = if (rowCount == 0) 0f else items.first().run { - val rowIndex = index / spanCount - (estimatedItemSize * rowIndex - offset.y) / totalSize * canvasSize +): Modifier = + drawScrollbar(Orientation.Vertical, reverseScrolling) { reverseDirection, atEnd, color, alpha -> + val layoutInfo = state.layoutInfo + val viewportSize = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset + val items = layoutInfo.visibleItemsInfo + val rowCount = (items.size + spanCount - 1) / spanCount + var itemsSize = 0 + for (i in 0 until rowCount) { + itemsSize += items[i * spanCount].size.height + } + if (items.size < layoutInfo.totalItemsCount || itemsSize > viewportSize) { + val estimatedItemSize = if (rowCount == 0) 0f else itemsSize.toFloat() / rowCount + val totalRow = (layoutInfo.totalItemsCount + spanCount - 1) / spanCount + val totalSize = estimatedItemSize * totalRow + val canvasSize = size.height + val thumbSize = viewportSize / totalSize * canvasSize + val startOffset = + if (rowCount == 0) 0f + else + items.first().run { + val rowIndex = index / spanCount + (estimatedItemSize * rowIndex - offset.y) / totalSize * canvasSize + } + drawScrollbar( + Orientation.Vertical, + reverseDirection, + atEnd, + color, + alpha, + thumbSize, + startOffset + ) } - drawScrollbar( - Orientation.Vertical, reverseDirection, atEnd, color, alpha, thumbSize, startOffset - ) } -} private fun DrawScope.drawScrollbar( orientation: Orientation, @@ -172,40 +193,35 @@ private fun DrawScope.drawScrollbar( startOffset: Float ) { val thicknessPx = Thickness.toPx() - val topLeft = if (orientation == Orientation.Horizontal) { - Offset( - if (reverseDirection) size.width - startOffset - thumbSize else startOffset, - if (atEnd) size.height - thicknessPx else 0f - ) - } else { - Offset( - if (atEnd) size.width - thicknessPx else 0f, - if (reverseDirection) size.height - startOffset - thumbSize else startOffset - ) - } - val size = if (orientation == Orientation.Horizontal) { - Size(thumbSize, thicknessPx) - } else { - Size(thicknessPx, thumbSize) - } + val topLeft = + if (orientation == Orientation.Horizontal) { + Offset( + if (reverseDirection) size.width - startOffset - thumbSize else startOffset, + if (atEnd) size.height - thicknessPx else 0f + ) + } else { + Offset( + if (atEnd) size.width - thicknessPx else 0f, + if (reverseDirection) size.height - startOffset - thumbSize else startOffset + ) + } + val size = + if (orientation == Orientation.Horizontal) { + Size(thumbSize, thicknessPx) + } else { + Size(thicknessPx, thumbSize) + } - drawRect( - color = color, - topLeft = topLeft, - size = size, - alpha = alpha() - ) + drawRect(color = color, topLeft = topLeft, size = size, alpha = alpha()) } private fun Modifier.drawScrollbar( orientation: Orientation, reverseScrolling: Boolean, - onDraw: DrawScope.( - reverseDirection: Boolean, - atEnd: Boolean, - color: Color, - alpha: () -> Float - ) -> Unit + onDraw: + DrawScope.( + reverseDirection: Boolean, atEnd: Boolean, color: Color, alpha: () -> Float + ) -> Unit ): Modifier = composed { val scrolled = remember { MutableSharedFlow<Unit>( @@ -213,19 +229,21 @@ private fun Modifier.drawScrollbar( onBufferOverflow = BufferOverflow.DROP_OLDEST ) } - val nestedScrollConnection = remember(orientation, scrolled) { - object : NestedScrollConnection { - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource - ): Offset { - val delta = if (orientation == Orientation.Horizontal) consumed.x else consumed.y - if (delta != 0f) scrolled.tryEmit(Unit) - return Offset.Zero + val nestedScrollConnection = + remember(orientation, scrolled) { + object : NestedScrollConnection { + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset { + val delta = + if (orientation == Orientation.Horizontal) consumed.x else consumed.y + if (delta != 0f) scrolled.tryEmit(Unit) + return Offset.Zero + } } } - } val alpha = remember { Animatable(0f) } LaunchedEffect(scrolled, alpha) { @@ -237,19 +255,18 @@ private fun Modifier.drawScrollbar( } val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr - val reverseDirection = if (orientation == Orientation.Horizontal) { - if (isLtr) reverseScrolling else !reverseScrolling - } else reverseScrolling + val reverseDirection = + if (orientation == Orientation.Horizontal) { + if (isLtr) reverseScrolling else !reverseScrolling + } else reverseScrolling val atEnd = if (orientation == Orientation.Vertical) isLtr else true val color = BarColor - Modifier - .nestedScroll(nestedScrollConnection) - .drawWithContent { - drawContent() - onDraw(reverseDirection, atEnd, color, alpha::value) - } + Modifier.nestedScroll(nestedScrollConnection).drawWithContent { + drawContent() + onDraw(reverseDirection, atEnd, color, alpha::value) + } } private val BarColor: Color @@ -264,17 +281,10 @@ private val FadeOutAnimationSpec = internal fun ScrollbarPreview() { val state = rememberScrollState() Column( - modifier = Modifier - .drawVerticalScrollbar(state) - .verticalScroll(state), + modifier = Modifier.drawVerticalScrollbar(state).verticalScroll(state), ) { repeat(50) { - Text( - text = "Item ${it + 1}", - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) + Text(text = "Item ${it + 1}", modifier = Modifier.fillMaxWidth().padding(16.dp)) } } } @@ -283,17 +293,9 @@ internal fun ScrollbarPreview() { @Composable internal fun LazyListScrollbarPreview() { val state = rememberLazyListState() - LazyColumn( - modifier = Modifier.drawVerticalScrollbar(state), - state = state - ) { + LazyColumn(modifier = Modifier.drawVerticalScrollbar(state), state = state) { items(50) { - Text( - text = "Item ${it + 1}", - modifier = Modifier - .fillMaxWidth() - .padding(16.dp) - ) + Text(text = "Item ${it + 1}", modifier = Modifier.fillMaxWidth().padding(16.dp)) } } } @@ -302,16 +304,11 @@ internal fun LazyListScrollbarPreview() { @Composable internal fun HorizontalScrollbarPreview() { val state = rememberScrollState() - Row( - modifier = Modifier - .drawHorizontalScrollbar(state) - .horizontalScroll(state) - ) { + Row(modifier = Modifier.drawHorizontalScrollbar(state).horizontalScroll(state)) { repeat(50) { Text( text = (it + 1).toString(), - modifier = Modifier - .padding(horizontal = 8.dp, vertical = 16.dp) + modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp) ) } } @@ -321,15 +318,11 @@ internal fun HorizontalScrollbarPreview() { @Composable internal fun LazyListHorizontalScrollbarPreview() { val state = rememberLazyListState() - LazyRow( - modifier = Modifier.drawHorizontalScrollbar(state), - state = state - ) { + LazyRow(modifier = Modifier.drawHorizontalScrollbar(state), state = state) { items(50) { Text( text = (it + 1).toString(), - modifier = Modifier - .padding(horizontal = 8.dp, vertical = 16.dp) + modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp) ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt index b30ca26f22..1b1117afb9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Switch.kt @@ -27,10 +27,7 @@ import net.mullvad.mullvadvpn.compose.theme.MullvadWhite @Preview @Composable private fun PreviewSwitch() { - CellSwitch( - isChecked = false, - onCheckedChange = null - ) + CellSwitch(isChecked = false, onCheckedChange = null) } @Composable @@ -49,49 +46,48 @@ fun CellSwitch( val thumbRadius = 11.dp // To move the thumb, we need to calculate the position (along x axis) - val animatePosition = animateFloatAsState( - targetValue = if (isChecked) - with(LocalDensity.current) { - (width - thumbRadius - gapBetweenThumbAndTrackEdge - 1.dp).toPx() - } - else - with(LocalDensity.current) { (thumbRadius + gapBetweenThumbAndTrackEdge + 1.dp).toPx() } - ) + val animatePosition = + animateFloatAsState( + targetValue = + if (isChecked) + with(LocalDensity.current) { + (width - thumbRadius - gapBetweenThumbAndTrackEdge - 1.dp).toPx() + } + else + with(LocalDensity.current) { + (thumbRadius + gapBetweenThumbAndTrackEdge + 1.dp).toPx() + } + ) Canvas( - modifier = modifier - .padding(1.dp) - .size(width = width, height = height) - .scale(scale = scale) - .pointerInput(Unit) { - if (onCheckedChange != null) { - detectTapGestures( - onTap = { - onCheckedChange(!isChecked) - } - ) + modifier = + modifier + .padding(1.dp) + .size(width = width, height = height) + .scale(scale = scale) + .pointerInput(Unit) { + if (onCheckedChange != null) { + detectTapGestures(onTap = { onCheckedChange(!isChecked) }) + } } - } ) { // Track drawRoundRect( color = thumbColor, cornerRadius = CornerRadius(x = 15.dp.toPx(), y = 15.dp.toPx()), - style = Stroke( - width = 2.dp.toPx(), - miter = 6.dp.toPx(), - cap = StrokeCap.Square, - ), + style = + Stroke( + width = 2.dp.toPx(), + miter = 6.dp.toPx(), + cap = StrokeCap.Square, + ), ) // Thumb drawCircle( color = if (isChecked) thumbCheckedTrackColor else thumbUncheckedTrackColor, radius = thumbRadius.toPx(), - center = Offset( - x = animatePosition.value, - y = size.height / 2 - ) + center = Offset(x = animatePosition.value, y = size.height / 2) ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Theme.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Theme.kt index 4cb68f9d17..8257dab2db 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Theme.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/Theme.kt @@ -4,10 +4,6 @@ import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable @Composable -fun AppTheme( - content: @Composable () -> Unit -) { - MaterialTheme( - content = content - ) +fun AppTheme(content: @Composable () -> Unit) { + MaterialTheme(content = content) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt index a0a9d4dd90..4395f1b0cf 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/component/TopBar.kt @@ -24,10 +24,7 @@ import net.mullvad.mullvadvpn.R @Preview @Composable private fun PreviewTopBar() { - TopBar( - backgroundColor = colorResource(R.color.blue), - onSettingsClicked = {} - ) + TopBar(backgroundColor = colorResource(R.color.blue), onSettingsClicked = {}) } @Composable @@ -38,11 +35,11 @@ fun TopBar( isIconAndLogoVisible: Boolean = true ) { ConstraintLayout( - modifier = Modifier - .fillMaxWidth() - .height(dimensionResource(id = R.dimen.top_bar_height)) - .background(backgroundColor) - .then(modifier), + modifier = + Modifier.fillMaxWidth() + .height(dimensionResource(id = R.dimen.top_bar_height)) + .background(backgroundColor) + .then(modifier), ) { val (logo, appName, settingsIcon) = createRefs() @@ -50,10 +47,8 @@ fun TopBar( Image( painter = painterResource(id = R.drawable.logo_icon), contentDescription = null, // No meaningful user info or action. - modifier = Modifier - .width(44.dp) - .height(44.dp) - .constrainAs(logo) { + modifier = + Modifier.width(44.dp).height(44.dp).constrainAs(logo) { centerVerticallyTo(parent) start.linkTo(parent.start, margin = 16.dp) } @@ -63,9 +58,8 @@ fun TopBar( painter = painterResource(id = R.drawable.logo_text), tint = colorResource(id = R.color.white80), contentDescription = null, // No meaningful user info or action. - modifier = Modifier - .height(16.dp) - .constrainAs(appName) { + modifier = + Modifier.height(16.dp).constrainAs(appName) { centerVerticallyTo(parent) start.linkTo(logo.end, margin = 8.dp) } @@ -76,13 +70,11 @@ fun TopBar( Image( painter = painterResource(R.drawable.icon_settings), contentDescription = stringResource(id = R.string.settings), - modifier = Modifier - .clickable { onSettingsClicked() } - .fillMaxHeight() - .padding(horizontal = 16.dp) - .constrainAs(settingsIcon) { - end.linkTo(parent.end) - } + modifier = + Modifier.clickable { onSettingsClicked() } + .fillMaxHeight() + .padding(horizontal = 16.dp) + .constrainAs(settingsIcon) { end.linkTo(parent.end) } ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt index 419f0fc7f7..cca158c3c0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/ChangelogDialog.kt @@ -24,15 +24,9 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.compose.component.ChangeListItem @Composable -fun ChangelogDialog( - changesList: List<String>, - version: String, - onDismiss: () -> Unit -) { +fun ChangelogDialog(changesList: List<String>, version: String, onDismiss: () -> Unit) { AlertDialog( - onDismissRequest = { - onDismiss() - }, + onDismissRequest = { onDismiss() }, title = { Text( text = version, @@ -40,47 +34,35 @@ fun ChangelogDialog( fontSize = 30.sp, fontStyle = FontStyle.Normal, textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() + modifier = Modifier.fillMaxWidth() ) }, - text = { Column { Text( text = stringResource(R.string.changes_dialog_subtitle), fontSize = 18.sp, color = Color.White, - modifier = Modifier - .padding( - vertical = dimensionResource(id = R.dimen.medium_padding) - ) + modifier = + Modifier.padding(vertical = dimensionResource(id = R.dimen.medium_padding)) ) - changesList.forEach { changeItem -> - ChangeListItem( - text = changeItem - ) - } + changesList.forEach { changeItem -> ChangeListItem(text = changeItem) } } }, buttons = { Button( - modifier = Modifier - .wrapContentHeight() - .padding(all = dimensionResource(id = R.dimen.medium_padding)) - .defaultMinSize( - minHeight = dimensionResource(id = R.dimen.button_height) - ) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = colorResource(id = R.color.blue), - contentColor = colorResource(id = R.color.white) - ), - onClick = { - onDismiss() - } - + modifier = + Modifier.wrapContentHeight() + .padding(all = dimensionResource(id = R.dimen.medium_padding)) + .defaultMinSize(minHeight = dimensionResource(id = R.dimen.button_height)) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.blue), + contentColor = colorResource(id = R.color.white) + ), + onClick = { onDismiss() } ) { Text( text = stringResource(R.string.changes_dialog_dismiss_button), @@ -88,10 +70,11 @@ fun ChangelogDialog( ) } }, - properties = DialogProperties( - dismissOnClickOutside = true, - dismissOnBackPress = true, - ), + properties = + DialogProperties( + dismissOnClickOutside = true, + dismissOnBackPress = true, + ), backgroundColor = colorResource(id = R.color.darkBlue) ) } @@ -99,19 +82,16 @@ fun ChangelogDialog( @Preview @Composable private fun PreviewChangelogDialogWithSingleShortItem() { - ChangelogDialog( - changesList = listOf("Item 1"), - version = "1111.1", - onDismiss = {} - ) + ChangelogDialog(changesList = listOf("Item 1"), version = "1111.1", onDismiss = {}) } @Preview @Composable private fun PreviewChangelogDialogWithTwoLongItems() { - val longPreviewText = "This is a sample changelog item of a Compose Preview visualization. " + - "The purpose of this specific sample text is to visualize a long text that will result " + - "in multiple lines in the changelog dialog." + val longPreviewText = + "This is a sample changelog item of a Compose Preview visualization. " + + "The purpose of this specific sample text is to visualize a long text that will result " + + "in multiple lines in the changelog dialog." ChangelogDialog( changesList = listOf(longPreviewText, longPreviewText), @@ -124,18 +104,19 @@ private fun PreviewChangelogDialogWithTwoLongItems() { @Composable private fun PreviewChangelogDialogWithTenShortItems() { ChangelogDialog( - changesList = listOf( - "Item 1", - "Item 2", - "Item 3", - "Item 4", - "Item 5", - "Item 6", - "Item 7", - "Item 8", - "Item 9", - "Item 10" - ), + changesList = + listOf( + "Item 1", + "Item 2", + "Item 3", + "Item 4", + "Item 5", + "Item 6", + "Item 7", + "Item 8", + "Item 9", + "Item 10" + ), version = "1111.1", onDismiss = {} ) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt index 228487e3cc..e83a4a42ac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DeviceRemovalDialog.kt @@ -34,91 +34,73 @@ import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel @Composable fun ShowDeviceRemovalDialog(viewModel: DeviceListViewModel, device: Device) { AlertDialog( - onDismissRequest = { - viewModel.clearStagedDevice() - }, + onDismissRequest = { viewModel.clearStagedDevice() }, title = { Column( horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(top = 0.dp) - .fillMaxWidth() + modifier = Modifier.padding(top = 0.dp).fillMaxWidth() ) { Image( painter = painterResource(id = R.drawable.icon_alert), contentDescription = "Remove", - modifier = Modifier - .width(50.dp) - .height(50.dp) + modifier = Modifier.width(50.dp).height(50.dp) ) } }, text = { - val htmlFormattedDialogText = textResource( - id = R.string.max_devices_confirm_removal_description, - device.name.capitalizeFirstCharOfEachWord() - ).let { introText -> - if (device.ports.isNotEmpty()) { - introText.plus(" " + stringResource(id = R.string.port_removal_notice)) - } else { - introText - } - } + val htmlFormattedDialogText = + textResource( + id = R.string.max_devices_confirm_removal_description, + device.name.capitalizeFirstCharOfEachWord() + ) + .let { introText -> + if (device.ports.isNotEmpty()) { + introText.plus(" " + stringResource(id = R.string.port_removal_notice)) + } else { + introText + } + } - HtmlText( - htmlFormattedString = htmlFormattedDialogText, - textSize = 16.sp.value - ) + HtmlText(htmlFormattedString = htmlFormattedDialogText, textSize = 16.sp.value) }, buttons = { - Column( - Modifier - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - ) { + Column(Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)) { Button( - modifier = Modifier - .height(dimensionResource(id = R.dimen.button_height)) - .defaultMinSize( - minWidth = 0.dp, - minHeight = dimensionResource(id = R.dimen.button_height) - ) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = colorResource(id = R.color.red), - contentColor = Color.White - ), - onClick = { - viewModel.confirmRemovalOfStagedDevice() - } + modifier = + Modifier.height(dimensionResource(id = R.dimen.button_height)) + .defaultMinSize( + minWidth = 0.dp, + minHeight = dimensionResource(id = R.dimen.button_height) + ) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.red), + contentColor = Color.White + ), + onClick = { viewModel.confirmRemovalOfStagedDevice() } ) { - Text( - text = stringResource(id = R.string.confirm_removal), - fontSize = 18.sp - ) + Text(text = stringResource(id = R.string.confirm_removal), fontSize = 18.sp) } Button( contentPadding = PaddingValues(0.dp), - modifier = Modifier - .focusOrder(FocusRequester()) - .padding(top = 16.dp) - .height(dimensionResource(id = R.dimen.button_height)) - .defaultMinSize( - minWidth = 0.dp, - minHeight = dimensionResource(id = R.dimen.button_height) - ) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = colorResource(id = R.color.blue), - contentColor = Color.White - ), - onClick = { - viewModel.clearStagedDevice() - } + modifier = + Modifier.focusOrder(FocusRequester()) + .padding(top = 16.dp) + .height(dimensionResource(id = R.dimen.button_height)) + .defaultMinSize( + minWidth = 0.dp, + minHeight = dimensionResource(id = R.dimen.button_height) + ) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.blue), + contentColor = Color.White + ), + onClick = { viewModel.clearStagedDevice() } ) { - Text( - text = stringResource(id = R.string.back), - fontSize = 18.sp - ) + Text(text = stringResource(id = R.string.back), fontSize = 18.sp) } } }, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt index b70aa2fddc..39869c9869 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/DnsDialog.kt @@ -58,9 +58,7 @@ fun DnsDialog( Dialog( // Fix for https://issuetracker.google.com/issues/221643630 properties = DialogProperties(usePlatformDefaultWidth = false), - onDismissRequest = { - onDismiss() - }, + onDismissRequest = { onDismiss() }, content = { Column( Modifier @@ -70,47 +68,46 @@ fun DnsDialog( .padding(dialogPadding) ) { Text( - text = if (stagedDns is StagedDns.NewDns) { - stringResource(R.string.add_dns_server_dialog_title) - } else { - stringResource(R.string.update_dns_server_dialog_title) - }, + text = + if (stagedDns is StagedDns.NewDns) { + stringResource(R.string.add_dns_server_dialog_title) + } else { + stringResource(R.string.update_dns_server_dialog_title) + }, color = Color.White, fontSize = textBigSize ) Box( - Modifier - .wrapContentSize() - .clickable { textFieldFocusRequester.requestFocus() } + Modifier.wrapContentSize().clickable { textFieldFocusRequester.requestFocus() } ) { DnsTextField( value = stagedDns.item.address, isValidValue = stagedDns.isValid(), - onValueChanged = { newMtuValue -> - onIpAddressChanged(newMtuValue) - }, + onValueChanged = { newMtuValue -> onIpAddressChanged(newMtuValue) }, onFocusChanges = {}, onSubmit = { onAttemptToSave() }, isEnabled = true, placeholderText = stringResource(R.string.enter_value_placeholder), - modifier = Modifier - .padding(top = midPadding) - .focusRequester(textFieldFocusRequester) + modifier = + Modifier.padding(top = midPadding) + .focusRequester(textFieldFocusRequester) ) } - val errorMessage = when { - stagedDns.validationResult is StagedDns.ValidationResult.DuplicateAddress -> { - stringResource(R.string.duplicate_address_warning) - } - stagedDns.item.isLocal && isAllowLanEnabled.not() -> { - stringResource(id = R.string.confirm_local_dns) + val errorMessage = + when { + stagedDns.validationResult is + StagedDns.ValidationResult.DuplicateAddress -> { + stringResource(R.string.duplicate_address_warning) + } + stagedDns.item.isLocal && isAllowLanEnabled.not() -> { + stringResource(id = R.string.confirm_local_dns) + } + else -> { + null + } } - else -> { - null - } - } if (errorMessage != null) { Text( @@ -122,17 +119,18 @@ fun DnsDialog( } Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .defaultMinSize(minHeight = buttonSize) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = MullvadWhite, - disabledContentColor = MullvadWhite60, - disabledBackgroundColor = MullvadWhite20 - ), + modifier = + Modifier.padding(top = mediumPadding) + .height(buttonSize) + .defaultMinSize(minHeight = buttonSize) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = MullvadWhite, + disabledContentColor = MullvadWhite60, + disabledBackgroundColor = MullvadWhite20 + ), onClick = { onAttemptToSave() }, enabled = stagedDns.isValid() ) { @@ -144,15 +142,16 @@ fun DnsDialog( if (stagedDns is StagedDns.EditDns) { Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .defaultMinSize(minHeight = buttonSize) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = MullvadWhite - ), + modifier = + Modifier.padding(top = mediumPadding) + .height(buttonSize) + .defaultMinSize(minHeight = buttonSize) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = MullvadWhite + ), onClick = { onRemove() } ) { Text( @@ -163,23 +162,19 @@ fun DnsDialog( } Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .defaultMinSize(minHeight = buttonSize) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = Color.White - ), - onClick = { - onDismiss() - } + modifier = + Modifier.padding(top = mediumPadding) + .height(buttonSize) + .defaultMinSize(minHeight = buttonSize) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = Color.White + ), + onClick = { onDismiss() } ) { - Text( - text = stringResource(id = R.string.cancel), - fontSize = textMediumSize - ) + Text(text = stringResource(id = R.string.cancel), fontSize = textMediumSize) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt index e4df8af467..222645c35a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/MtuDialog.kt @@ -52,15 +52,9 @@ fun MtuDialog( val smallPadding = 5.dp Dialog( - onDismissRequest = { - onDismiss() - }, + onDismissRequest = { onDismiss() }, content = { - Column( - Modifier - .background(color = MullvadDarkBlue) - .padding(dialogPadding) - ) { + Column(Modifier.background(color = MullvadDarkBlue).padding(dialogPadding)) { Text( text = stringResource(id = R.string.wireguard_mtu), color = Color.White, @@ -68,16 +62,13 @@ fun MtuDialog( ) Box( - Modifier - .wrapContentSize() + Modifier.wrapContentSize() .clickable { textFieldFocusRequester.requestFocus() } .padding(top = dialogPadding) ) { MtuTextField( value = mtuValue, - onValueChanged = { newMtuValue -> - onMtuValueChanged(newMtuValue) - }, + onValueChanged = { newMtuValue -> onMtuValueChanged(newMtuValue) }, onFocusChange = {}, onSubmit = { newMtuValue -> if (newMtuValue.toIntOrNull()?.isValidMtu() == true) { @@ -88,59 +79,50 @@ fun MtuDialog( placeholderText = stringResource(R.string.enter_value_placeholder), maxCharLength = 4, isValidValue = isValidMtu, - modifier = Modifier - .focusRequester(textFieldFocusRequester) + modifier = Modifier.focusRequester(textFieldFocusRequester) ) } Text( - text = stringResource( - id = R.string.wireguard_mtu_footer, - MTU_MIN_VALUE, - MTU_MAX_VALUE - ), + text = + stringResource( + id = R.string.wireguard_mtu_footer, + MTU_MIN_VALUE, + MTU_MAX_VALUE + ), fontSize = textSmallSize, color = MullvadWhite60, modifier = Modifier.padding(top = smallPadding) ) Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = MullvadWhite, - disabledContentColor = MullvadWhite60, - disabledBackgroundColor = MullvadWhite20 - ), + modifier = + Modifier.padding(top = mediumPadding).height(buttonSize).fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = MullvadWhite, + disabledContentColor = MullvadWhite60, + disabledBackgroundColor = MullvadWhite20 + ), enabled = isValidMtu, - onClick = { - onSave() - } + onClick = { onSave() } ) { - Text( - text = stringResource(R.string.submit_button), - fontSize = textMediumSize - ) + Text(text = stringResource(R.string.submit_button), fontSize = textMediumSize) } Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .defaultMinSize( - minHeight = buttonSize - ) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = MullvadWhite - ), - onClick = { - onRestoreDefaultValue() - } + modifier = + Modifier.padding(top = mediumPadding) + .height(buttonSize) + .defaultMinSize(minHeight = buttonSize) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = MullvadWhite + ), + onClick = { onRestoreDefaultValue() } ) { Text( text = stringResource(R.string.reset_to_default_button), @@ -149,25 +131,19 @@ fun MtuDialog( } Button( - modifier = Modifier - .padding(top = mediumPadding) - .height(buttonSize) - .defaultMinSize( - minHeight = buttonSize - ) - .fillMaxWidth(), - colors = ButtonDefaults.buttonColors( - backgroundColor = MullvadBlue, - contentColor = Color.White - ), - onClick = { - onDismiss() - } + modifier = + Modifier.padding(top = mediumPadding) + .height(buttonSize) + .defaultMinSize(minHeight = buttonSize) + .fillMaxWidth(), + colors = + ButtonDefaults.buttonColors( + backgroundColor = MullvadBlue, + contentColor = Color.White + ), + onClick = { onDismiss() } ) { - Text( - text = stringResource(R.string.cancel), - fontSize = textMediumSize - ) + Text(text = stringResource(R.string.cancel), fontSize = textMediumSize) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt index 6d67718f1a..ff0f471c88 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/AdvancedSettingScreen.kt @@ -46,13 +46,12 @@ import net.mullvad.mullvadvpn.viewmodel.CustomDnsItem @Composable private fun PreviewAdvancedSettings() { AdvancedSettingScreen( - uiState = AdvancedSettingsUiState.DefaultUiState( - mtu = "1337", - isCustomDnsEnabled = true, - customDnsItems = listOf( - CustomDnsItem("0.0.0.0", false) - ) - ), + uiState = + AdvancedSettingsUiState.DefaultUiState( + mtu = "1337", + isCustomDnsEnabled = true, + customDnsItems = listOf(CustomDnsItem("0.0.0.0", false)) + ), onMtuCellClick = {}, onMtuInputChange = {}, onSaveMtuClick = {}, @@ -121,28 +120,23 @@ fun AdvancedSettingScreen( val topPadding = 6.dp CollapsingToolbarTheme { - val state = rememberCollapsingToolbarScaffoldState() val progress = state.toolbarState.progress CollapsableAwareToolbarScaffold( - modifier = Modifier - .background(MullvadDarkBlue) - .fillMaxSize(), + modifier = Modifier.background(MullvadDarkBlue).fillMaxSize(), state = state, scrollStrategy = ScrollStrategy.ExitUntilCollapsed, isEnabledWhenCollapsable = true, toolbar = { - val scaffoldModifier = Modifier - .road( + val scaffoldModifier = + Modifier.road( whenCollapsed = Alignment.TopCenter, whenExpanded = Alignment.BottomStart ) CollapsingTopBar( backgroundColor = MullvadDarkBlue, - onBackClicked = { - onBackClick() - }, + onBackClicked = { onBackClick() }, title = stringResource(id = R.string.settings_advanced), progress = progress, modifier = scaffoldModifier, @@ -151,27 +145,19 @@ fun AdvancedSettingScreen( } ) { LazyColumn( - modifier = Modifier - .drawVerticalScrollbar(lazyListState) - .fillMaxWidth() - .wrapContentHeight() - .animateContentSize(), + modifier = + Modifier.drawVerticalScrollbar(lazyListState) + .fillMaxWidth() + .wrapContentHeight() + .animateContentSize(), state = lazyListState - ) { - item { - MtuComposeCell( - mtuValue = uiState.mtu, - onEditMtu = { onMtuCellClick() } - ) - } + item { MtuComposeCell(mtuValue = uiState.mtu, onEditMtu = { onMtuCellClick() }) } item { NavigationComposeCell( title = stringResource(id = R.string.split_tunneling), - onClick = { - onSplitTunnelingNavigationClick.invoke() - } + onClick = { onSplitTunnelingNavigationClick.invoke() } ) Divider() } @@ -179,9 +165,7 @@ fun AdvancedSettingScreen( item { CustomDnsComposeCell( checkboxDefaultState = uiState.isCustomDnsEnabled, - onToggle = { newValue -> - onToggleDnsClick(newValue) - } + onToggle = { newValue -> onToggleDnsClick(newValue) } ) Divider() } @@ -190,8 +174,8 @@ fun AdvancedSettingScreen( itemsIndexed(uiState.customDnsItems) { index, item -> DnsCell( address = item.address, - isUnreachableLocalDnsWarningVisible = item.isLocal && - uiState.isAllowLanEnabled.not(), + isUnreachableLocalDnsWarningVisible = + item.isLocal && uiState.isAllowLanEnabled.not(), onClick = { onDnsClick(index) }, modifier = Modifier.animateItemPlacement(), ) @@ -207,7 +191,7 @@ fun AdvancedSettingScreen( color = Color.White ) }, - bodyView = { }, + bodyView = {}, subtitle = null, background = MullvadBlue20, startPadding = biggerPadding @@ -218,8 +202,7 @@ fun AdvancedSettingScreen( item { CustomDnsCellSubtitle( - Modifier - .background(MullvadDarkBlue) + Modifier.background(MullvadDarkBlue) .padding( start = cellHorizontalSpacing, top = topPadding, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt index d0fb48348e..e0df9b7e8a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceListScreen.kt @@ -43,59 +43,52 @@ fun DeviceListScreen( val state = viewModel.uiState.collectAsState().value if (state.stagedDevice != null) { - ShowDeviceRemovalDialog( - viewModel = viewModel, - device = state.stagedDevice - ) + ShowDeviceRemovalDialog(viewModel = viewModel, device = state.stagedDevice) } ConstraintLayout( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - .background(colorResource(id = R.color.darkBlue)) + modifier = + Modifier.fillMaxHeight().fillMaxWidth().background(colorResource(id = R.color.darkBlue)) ) { val (content, buttons) = createRefs() Column( - modifier = Modifier - .constrainAs(content) { - top.linkTo(parent.top) - bottom.linkTo(buttons.top) - height = Dimension.fillToConstraints - width = Dimension.matchParent - } - .verticalScroll(rememberScrollState()) + modifier = + Modifier.constrainAs(content) { + top.linkTo(parent.top) + bottom.linkTo(buttons.top) + height = Dimension.fillToConstraints + width = Dimension.matchParent + } + .verticalScroll(rememberScrollState()) ) { - ConstraintLayout( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - ) { + ConstraintLayout(modifier = Modifier.fillMaxWidth().wrapContentHeight()) { val (icon, message, list) = createRefs() Image( - painter = painterResource( - id = if (state.hasTooManyDevices) { - R.drawable.icon_fail - } else { - R.drawable.icon_success - } - ), + painter = + painterResource( + id = + if (state.hasTooManyDevices) { + R.drawable.icon_fail + } else { + R.drawable.icon_success + } + ), contentDescription = null, // No meaningful user info or action. - modifier = Modifier - .constrainAs(icon) { - top.linkTo(parent.top, margin = 30.dp) - start.linkTo(parent.start) - end.linkTo(parent.end) - } - .width(64.dp) - .height(64.dp) + modifier = + Modifier.constrainAs(icon) { + top.linkTo(parent.top, margin = 30.dp) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + .width(64.dp) + .height(64.dp) ) Column( - modifier = Modifier - .constrainAs(message) { + modifier = + Modifier.constrainAs(message) { top.linkTo(icon.bottom, margin = 16.dp) start.linkTo(parent.start, margin = 22.dp) end.linkTo(parent.end, margin = 22.dp) @@ -103,38 +96,40 @@ fun DeviceListScreen( }, ) { Text( - text = stringResource( - id = if (state.hasTooManyDevices) { - R.string.max_devices_warning_title - } else { - R.string.max_devices_resolved_title - } - ), + text = + stringResource( + id = + if (state.hasTooManyDevices) { + R.string.max_devices_warning_title + } else { + R.string.max_devices_resolved_title + } + ), fontSize = 24.sp, color = Color.White, fontWeight = FontWeight.Bold ) Text( - text = stringResource( - id = if (state.hasTooManyDevices) { - R.string.max_devices_warning_description - } else { - R.string.max_devices_resolved_description - } - ), + text = + stringResource( + id = + if (state.hasTooManyDevices) { + R.string.max_devices_warning_description + } else { + R.string.max_devices_resolved_description + } + ), color = Color.White, fontSize = 14.sp, - modifier = Modifier - .wrapContentHeight() - .animateContentSize() - .padding(top = 8.dp) + modifier = + Modifier.wrapContentHeight().animateContentSize().padding(top = 8.dp) ) } Box( - modifier = Modifier - .constrainAs(list) { + modifier = + Modifier.constrainAs(list) { top.linkTo(message.bottom, margin = 20.dp) height = Dimension.wrapContent width = Dimension.matchParent @@ -156,8 +151,8 @@ fun DeviceListScreen( } Column( - modifier = Modifier - .constrainAs(buttons) { + modifier = + Modifier.constrainAs(buttons) { bottom.linkTo(parent.bottom, margin = 22.dp) start.linkTo(parent.start, margin = 22.dp) end.linkTo(parent.end, margin = 22.dp) @@ -168,23 +163,24 @@ fun DeviceListScreen( text = stringResource(id = R.string.continue_login), onClick = onContinueWithLogin, isEnabled = state.hasTooManyDevices.not() && state.isLoading.not(), - colors = ButtonDefaults.buttonColors( - backgroundColor = colorResource(id = R.color.green), - disabledBackgroundColor = colorResource(id = R.color.green40), - disabledContentColor = colorResource(id = R.color.white80), - contentColor = Color.White - ) + colors = + ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.green), + disabledBackgroundColor = colorResource(id = R.color.green40), + disabledContentColor = colorResource(id = R.color.white80), + contentColor = Color.White + ) ) ActionButton( text = stringResource(id = R.string.back), onClick = onBackClick, - colors = ButtonDefaults.buttonColors( - backgroundColor = colorResource(id = R.color.blue), - contentColor = Color.White - ), - modifier = Modifier - .padding(top = 16.dp) + colors = + ButtonDefaults.buttonColors( + backgroundColor = colorResource(id = R.color.blue), + contentColor = Color.White + ), + modifier = Modifier.padding(top = 16.dp) ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt index ce92ef72aa..dcbc32befb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/DeviceRevokedScreen.kt @@ -28,36 +28,32 @@ import net.mullvad.mullvadvpn.compose.state.DeviceRevokedUiState import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedViewModel @Composable -fun DeviceRevokedScreen( - deviceRevokedViewModel: DeviceRevokedViewModel -) { +fun DeviceRevokedScreen(deviceRevokedViewModel: DeviceRevokedViewModel) { val state = deviceRevokedViewModel.uiState.collectAsState().value ConstraintLayout( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - .background(colorResource(id = R.color.darkBlue)) + modifier = + Modifier.fillMaxHeight().fillMaxWidth().background(colorResource(id = R.color.darkBlue)) ) { val (icon, body, actionButtons) = createRefs() Image( painter = painterResource(id = R.drawable.icon_fail), contentDescription = null, // No meaningful user info or action. - modifier = Modifier - .constrainAs(icon) { - top.linkTo(parent.top, margin = 30.dp) - start.linkTo(parent.start) - end.linkTo(parent.end) - } - .padding(horizontal = 12.dp) - .width(80.dp) - .height(80.dp) + modifier = + Modifier.constrainAs(icon) { + top.linkTo(parent.top, margin = 30.dp) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + .padding(horizontal = 12.dp) + .width(80.dp) + .height(80.dp) ) Column( - modifier = Modifier - .constrainAs(body) { + modifier = + Modifier.constrainAs(body) { top.linkTo(icon.bottom, margin = 22.dp) start.linkTo(parent.start, margin = 22.dp) end.linkTo(parent.end, margin = 22.dp) @@ -89,8 +85,8 @@ fun DeviceRevokedScreen( } Column( - modifier = Modifier - .constrainAs(actionButtons) { + modifier = + Modifier.constrainAs(actionButtons) { bottom.linkTo(parent.bottom, margin = 22.dp) start.linkTo(parent.start, margin = 22.dp) end.linkTo(parent.end, margin = 22.dp) @@ -100,16 +96,18 @@ fun DeviceRevokedScreen( ActionButton( text = stringResource(id = R.string.go_to_login), onClick = { deviceRevokedViewModel.onGoToLoginClicked() }, - colors = ButtonDefaults.buttonColors( - contentColor = Color.White, - backgroundColor = colorResource( - if (state == DeviceRevokedUiState.SECURED) { - R.color.red60 - } else { - R.color.blue - } + colors = + ButtonDefaults.buttonColors( + contentColor = Color.White, + backgroundColor = + colorResource( + if (state == DeviceRevokedUiState.SECURED) { + R.color.red60 + } else { + R.color.blue + } + ) ) - ) ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt index 4869cf6e9c..0189fc4df6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoadingScreen.kt @@ -29,9 +29,7 @@ private fun PreviewLoadingScreen() { } @Composable -fun LoadingScreen( - onSettingsCogClicked: () -> Unit -) { +fun LoadingScreen(onSettingsCogClicked: () -> Unit) { val backgroundColor = colorResource(id = R.color.blue) ScaffoldWithTopBar( @@ -43,10 +41,8 @@ fun LoadingScreen( content = { Box( contentAlignment = Alignment.Center, - modifier = Modifier - .background(backgroundColor) - .padding(bottom = 64.dp) - .fillMaxSize() + modifier = + Modifier.background(backgroundColor).padding(bottom = 64.dp).fillMaxSize() ) { Column( verticalArrangement = Arrangement.Center, @@ -55,23 +51,19 @@ fun LoadingScreen( Image( painter = painterResource(id = R.drawable.launch_logo), contentDescription = "", - modifier = Modifier - .size(120.dp) + modifier = Modifier.size(120.dp) ) Image( painter = painterResource(id = R.drawable.logo_text), contentDescription = "", alpha = 0.6f, - modifier = Modifier - .padding(top = 12.dp) - .height(18.dp) + modifier = Modifier.padding(top = 12.dp).height(18.dp) ) Text( text = stringResource(id = R.string.connecting_to_daemon), fontSize = 13.sp, color = colorResource(id = R.color.white40), - modifier = Modifier - .padding(top = 12.dp) + modifier = Modifier.padding(top = 12.dp) ) } } 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 8ef4f59853..7c1cfdcf1a 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 @@ -37,17 +37,15 @@ fun PrivacyDisclaimerScreen( onAcceptClicked: () -> Unit, ) { ConstraintLayout( - modifier = Modifier - .fillMaxHeight() - .fillMaxWidth() - .background(colorResource(id = R.color.darkBlue)) + modifier = + Modifier.fillMaxHeight().fillMaxWidth().background(colorResource(id = R.color.darkBlue)) ) { val (body, actionButtons) = createRefs() val sideMargin = dimensionResource(id = R.dimen.side_margin) Column( - modifier = Modifier - .constrainAs(body) { + modifier = + Modifier.constrainAs(body) { top.linkTo(parent.top, margin = sideMargin) start.linkTo(parent.start, margin = sideMargin) end.linkTo(parent.end, margin = sideMargin) @@ -68,34 +66,33 @@ fun PrivacyDisclaimerScreen( modifier = Modifier.padding(top = 10.dp) ) - Row( - modifier = Modifier.padding(top = 10.dp) - ) { + Row(modifier = Modifier.padding(top = 10.dp)) { ClickableText( text = AnnotatedString(stringResource(id = R.string.privacy_policy_label)), onClick = { onPrivacyPolicyLinkClicked.invoke() }, - style = TextStyle( - fontSize = 12.sp, - color = Color.White, - textDecoration = TextDecoration.Underline - ) + style = + TextStyle( + fontSize = 12.sp, + color = Color.White, + textDecoration = TextDecoration.Underline + ) ) Image( painter = painterResource(id = R.drawable.icon_extlink), contentDescription = null, - modifier = Modifier - .align(Alignment.CenterVertically) - .padding(start = 2.dp, top = 2.dp) - .width(10.dp) - .height(10.dp) + modifier = + Modifier.align(Alignment.CenterVertically) + .padding(start = 2.dp, top = 2.dp) + .width(10.dp) + .height(10.dp) ) } } Column( - modifier = Modifier - .constrainAs(actionButtons) { + modifier = + Modifier.constrainAs(actionButtons) { bottom.linkTo(parent.bottom, margin = sideMargin) start.linkTo(parent.start, margin = sideMargin) end.linkTo(parent.end, margin = sideMargin) @@ -105,12 +102,11 @@ fun PrivacyDisclaimerScreen( ActionButton( text = stringResource(id = R.string.agree_and_continue), onClick = onAcceptClicked::invoke, - colors = ButtonDefaults.buttonColors( - contentColor = Color.White, - backgroundColor = colorResource( - R.color.blue + colors = + ButtonDefaults.buttonColors( + contentColor = Color.White, + backgroundColor = colorResource(R.color.blue) ) - ) ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt index e989960301..e22aaffde2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/DeviceListUiState.kt @@ -10,15 +10,9 @@ data class DeviceListUiState( val hasTooManyDevices = deviceUiItems.count() >= 5 companion object { - val INITIAL = DeviceListUiState( - deviceUiItems = emptyList(), - isLoading = true, - stagedDevice = null - ) + val INITIAL = + DeviceListUiState(deviceUiItems = emptyList(), isLoading = true, stagedDevice = null) } } -data class DeviceListItemUiState( - val device: Device, - val isLoading: Boolean -) +data class DeviceListItemUiState(val device: Device, val isLoading: Boolean) 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 3a263a6886..4535971a69 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 @@ -68,23 +68,26 @@ fun CustomTextField( var isFocused by remember { mutableStateOf(false) } - val textColor = when { - isValidValue.not() -> Color.Red - isFocused -> MullvadBlue - else -> defaultTextColor - } + val textColor = + when { + isValidValue.not() -> Color.Red + isFocused -> MullvadBlue + else -> defaultTextColor + } - val placeholderTextColor = if (isFocused) { - placeHolderColor - } else { - Color.White - } + val placeholderTextColor = + if (isFocused) { + placeHolderColor + } else { + Color.White + } - val backgroundColor = if (isFocused) { - Color.White - } else { - MullvadWhite10 - } + val backgroundColor = + if (isFocused) { + Color.White + } else { + MullvadWhite10 + } fun triggerSubmit() { keyboardController?.hide() @@ -101,28 +104,19 @@ fun CustomTextField( onValueChanged(input.replace(NEWLINE_STRING, EMPTY_STRING)) } }, - textStyle = TextStyle( - color = textColor, - fontSize = fontSize, - textAlign = textAlign - ), + textStyle = TextStyle(color = textColor, fontSize = fontSize, textAlign = textAlign), enabled = isEnabled, singleLine = true, maxLines = 1, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done, - autoCorrect = false, - ), - keyboardActions = KeyboardActions( - onDone = { triggerSubmit() } - ), + keyboardOptions = + KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done, + autoCorrect = false, + ), + keyboardActions = KeyboardActions(onDone = { triggerSubmit() }), decorationBox = { decorationBox -> - Box( - modifier = Modifier - .padding(PaddingValues(12.dp, 10.dp)) - .fillMaxWidth() - ) { + Box(modifier = Modifier.padding(PaddingValues(12.dp, 10.dp)).fillMaxWidth()) { if (value.isBlank()) { Text( text = placeholderText, @@ -136,37 +130,38 @@ fun CustomTextField( } }, cursorBrush = SolidColor(MullvadBlue), - modifier = modifier - .background(backgroundColor) - .clip(shape) - .onFocusChanged { focusState -> - isFocused = focusState.isFocused - onFocusChange(focusState.isFocused) - } - .height(textFieldHeight) - .onKeyEvent { keyEvent -> - return@onKeyEvent when (keyEvent.nativeKeyEvent.keyCode) { - KeyEvent.KEYCODE_ENTER -> { - triggerSubmit() - true - } - KeyEvent.KEYCODE_ESCAPE -> { - focusManager.clearFocus(force = true) - keyboardController?.hide() - true - } - KeyEvent.KEYCODE_DPAD_DOWN -> { - focusManager.moveFocus(FocusDirection.Down) - true - } - KeyEvent.KEYCODE_DPAD_UP -> { - focusManager.moveFocus(FocusDirection.Up) - true - } - else -> { - false + modifier = + modifier + .background(backgroundColor) + .clip(shape) + .onFocusChanged { focusState -> + isFocused = focusState.isFocused + onFocusChange(focusState.isFocused) + } + .height(textFieldHeight) + .onKeyEvent { keyEvent -> + return@onKeyEvent when (keyEvent.nativeKeyEvent.keyCode) { + KeyEvent.KEYCODE_ENTER -> { + triggerSubmit() + true + } + KeyEvent.KEYCODE_ESCAPE -> { + focusManager.clearFocus(force = true) + keyboardController?.hide() + true + } + KeyEvent.KEYCODE_DPAD_DOWN -> { + focusManager.moveFocus(FocusDirection.Down) + true + } + KeyEvent.KEYCODE_DPAD_UP -> { + focusManager.moveFocus(FocusDirection.Up) + true + } + else -> { + false + } } } - } ) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/DnsTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/DnsTextField.kt index 198bb59159..3454f48792 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/DnsTextField.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/DnsTextField.kt @@ -9,9 +9,9 @@ fun DnsTextField( value: String, isValidValue: Boolean, modifier: Modifier = Modifier, - onValueChanged: (String) -> Unit = { }, - onFocusChanges: (Boolean) -> Unit = { }, - onSubmit: (String) -> Unit = { }, + onValueChanged: (String) -> Unit = {}, + onFocusChanges: (Boolean) -> Unit = {}, + onSubmit: (String) -> Unit = {}, placeholderText: String = "", isEnabled: Boolean = true ) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/MtuTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/MtuTextField.kt index c44c16911c..787c502908 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/MtuTextField.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/MtuTextField.kt @@ -8,9 +8,9 @@ fun MtuTextField( value: String, isValidValue: Boolean, modifier: Modifier = Modifier, - onValueChanged: (String) -> Unit = { }, - onFocusChange: (Boolean) -> Unit = { }, - onSubmit: (String) -> Unit = { }, + onValueChanged: (String) -> Unit = {}, + onFocusChange: (Boolean) -> Unit = {}, + onSubmit: (String) -> Unit = {}, isEnabled: Boolean = true, placeholderText: String = "", maxCharLength: Int diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Theme.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Theme.kt index 95e6d312a0..e02626029e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Theme.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/theme/Theme.kt @@ -7,27 +7,19 @@ import androidx.compose.material.lightColors import androidx.compose.runtime.Composable import androidx.compose.ui.unit.dp -private val MullvadColorPalette = lightColors( - primary = MullvadBlue, - primaryVariant = MullvadDarkBlue, - secondary = MullvadRed -) +private val MullvadColorPalette = + lightColors(primary = MullvadBlue, primaryVariant = MullvadDarkBlue, secondary = MullvadRed) -val Shapes = Shapes( - small = RoundedCornerShape(4.dp), - medium = RoundedCornerShape(4.dp), - large = RoundedCornerShape(0.dp) -) +val Shapes = + Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) + ) @Composable -fun CollapsingToolbarTheme( - content: @Composable () -> Unit -) { +fun CollapsingToolbarTheme(content: @Composable () -> Unit) { val colors = MullvadColorPalette - MaterialTheme( - colors = colors, - shapes = Shapes, - content = content - ) + MaterialTheme(colors = colors, shapes = Shapes, content = content) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt index 89df8ac582..fb09846b8b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/MullvadProblemReport.kt @@ -26,9 +26,8 @@ class MullvadProblemReport { private val commandChannel = spawnActor() - private val problemReportPath = GlobalScope.async(Dispatchers.Default) { - File(logDirectory.await(), PROBLEM_REPORT_FILE) - } + private val problemReportPath = + GlobalScope.async(Dispatchers.Default) { File(logDirectory.await(), PROBLEM_REPORT_FILE) } private var isCollected = false @@ -65,21 +64,21 @@ class MullvadProblemReport { commandChannel.trySendBlocking(Command.Delete()) } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - while (true) { - val command = channel.receive() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + while (true) { + val command = channel.receive() - when (command) { - is Command.Collect -> doCollect() - is Command.Load -> command.logs.complete(doLoad()) - is Command.Send -> command.result.complete(doSend()) - is Command.Delete -> doDelete() + when (command) { + is Command.Collect -> doCollect() + is Command.Load -> command.logs.complete(doLoad()) + is Command.Send -> command.result.complete(doSend()) + is Command.Delete -> doDelete() + } } - } - } catch (exception: ClosedReceiveChannelException) { + } catch (exception: ClosedReceiveChannelException) {} } - } private suspend fun doCollect() { val logDirectoryPath = logDirectory.await().absolutePath @@ -107,13 +106,14 @@ class MullvadProblemReport { doCollect() } - val result = isCollected && - sendProblemReport( - userEmail, - userMessage, - problemReportPath.await().absolutePath, - cacheDirectory.await().absolutePath - ) + val result = + isCollected && + sendProblemReport( + userEmail, + userMessage, + problemReportPath.await().absolutePath, + cacheDirectory.await().absolutePath + ) if (result) { doDelete() 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 4a95b2046c..8427ae0773 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 @@ -38,7 +38,6 @@ import org.koin.dsl.module import org.koin.dsl.onClose val uiModule = module { - single<SharedPreferences>(named(APP_PREFERENCES_NAME)) { androidApplication().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) } @@ -73,10 +72,7 @@ val uiModule = module { single { DeviceRepository(get()) } single { PrivacyDisclaimerRepository( - androidContext().getSharedPreferences( - APP_PREFERENCES_NAME, - Context.MODE_PRIVATE - ) + androidContext().getSharedPreferences(APP_PREFERENCES_NAME, Context.MODE_PRIVATE) ) } single { SettingsRepository(get()) } @@ -89,19 +85,10 @@ val uiModule = module { viewModel { DeviceListViewModel(get(), get()) } viewModel { LoginViewModel(get(), get()) } viewModel { - ChangelogViewModel( - get(), - BuildConfig.VERSION_CODE, - BuildConfig.ALWAYS_SHOW_CHANGELOG - ) + ChangelogViewModel(get(), BuildConfig.VERSION_CODE, BuildConfig.ALWAYS_SHOW_CHANGELOG) } viewModel { PrivacyDisclaimerViewModel(get()) } - viewModel { - AdvancedSettingsViewModel( - repository = get(), - inetAddressValidator = get() - ) - } + viewModel { AdvancedSettingsViewModel(repository = get(), inetAddressValidator = get()) } } const val APPS_SCOPE = "APPS_SCOPE" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt index 12df45f923..431023caa2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/VpnServiceModule.kt @@ -4,6 +4,4 @@ import androidx.core.app.NotificationManagerCompat import org.koin.android.ext.koin.androidContext import org.koin.dsl.module -val vpnServiceModule = module { - single { NotificationManagerCompat.from(androidContext()) } -} +val vpnServiceModule = module { single { NotificationManagerCompat.from(androidContext()) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/DispatchingHandler.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/DispatchingHandler.kt index 93c79a1ab9..b105d2192d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/DispatchingHandler.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/DispatchingHandler.kt @@ -8,19 +8,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.withLock import kotlin.reflect.KClass -class DispatchingHandler<T : Any>( - looper: Looper, - private val extractor: (Message) -> T? -) : Handler(looper), MessageDispatcher<T> { +class DispatchingHandler<T : Any>(looper: Looper, private val extractor: (Message) -> T?) : + Handler(looper), MessageDispatcher<T> { private val handlers = HashMap<KClass<out T>, (T) -> Unit>() private val lock = ReentrantReadWriteLock() override fun <V : T> registerHandler(variant: KClass<V>, handler: (V) -> Unit) { lock.writeLock().withLock { - handlers.put(variant) { instance -> - @Suppress("UNCHECKED_CAST") - handler(instance as V) - } + handlers.put(variant) { instance -> @Suppress("UNCHECKED_CAST") handler(instance as V) } } } @@ -39,9 +34,7 @@ class DispatchingHandler<T : Any>( } fun onDestroy() { - lock.writeLock().withLock { - handlers.clear() - } + lock.writeLock().withLock { handlers.clear() } removeCallbacksAndMessages(null) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt index ea7ecdcd7b..99267b3363 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Event.kt @@ -21,53 +21,38 @@ import net.mullvad.mullvadvpn.model.VoucherSubmissionResult as VoucherSubmission sealed class Event : Message.EventMessage() { protected override val messageKey = MESSAGE_KEY - @Parcelize - data class AccountCreationEvent(val result: AccountCreationResult) : Event() + @Parcelize data class AccountCreationEvent(val result: AccountCreationResult) : Event() - @Parcelize - data class AccountExpiryEvent(val expiry: AccountExpiry) : Event() + @Parcelize data class AccountExpiryEvent(val expiry: AccountExpiry) : Event() - @Parcelize - data class AccountHistoryEvent(val history: AccountHistory) : Event() + @Parcelize data class AccountHistoryEvent(val history: AccountHistory) : Event() - @Parcelize - data class AppVersionInfo(val versionInfo: AppVersionInfoData?) : Event() + @Parcelize data class AppVersionInfo(val versionInfo: AppVersionInfoData?) : Event() - @Parcelize - data class AuthToken(val token: String?) : Event() + @Parcelize data class AuthToken(val token: String?) : Event() - @Parcelize - data class CurrentVersion(val version: String?) : Event() + @Parcelize data class CurrentVersion(val version: String?) : Event() - @Parcelize - data class DeviceStateEvent(val newState: DeviceState) : Event() + @Parcelize data class DeviceStateEvent(val newState: DeviceState) : Event() - @Parcelize - data class DeviceListUpdate(val event: DeviceListEvent) : Event() + @Parcelize data class DeviceListUpdate(val event: DeviceListEvent) : Event() @Parcelize data class DeviceRemovalEvent(val deviceId: String, val result: RemoveDeviceResult) : Event() - @Parcelize - data class ListenerReady(val connection: Messenger, val listenerId: Int) : Event() + @Parcelize data class ListenerReady(val connection: Messenger, val listenerId: Int) : Event() - @Parcelize - data class LoginEvent(val result: LoginResult) : Event() + @Parcelize data class LoginEvent(val result: LoginResult) : Event() - @Parcelize - data class NewLocation(val location: GeoIpLocation?) : Event() + @Parcelize data class NewLocation(val location: GeoIpLocation?) : Event() - @Parcelize - data class NewRelayList(val relayList: RelayList?) : Event() + @Parcelize data class NewRelayList(val relayList: RelayList?) : Event() - @Parcelize - data class SettingsUpdate(val settings: Settings?) : Event() + @Parcelize data class SettingsUpdate(val settings: Settings?) : Event() - @Parcelize - data class SplitTunnelingUpdate(val excludedApps: List<String>?) : Event() + @Parcelize data class SplitTunnelingUpdate(val excludedApps: List<String>?) : Event() - @Parcelize - data class TunnelStateChange(val tunnelState: TunnelState) : Event() + @Parcelize data class TunnelStateChange(val tunnelState: TunnelState) : Event() @Parcelize data class VoucherSubmissionResult( @@ -75,8 +60,7 @@ sealed class Event : Message.EventMessage() { val result: VoucherSubmissionResultData ) : Event() - @Parcelize - object VpnPermissionRequest : Event() + @Parcelize object VpnPermissionRequest : Event() companion object { private const val MESSAGE_KEY = "event" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlow.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlow.kt index de512c7cc4..b16cfe9fbd 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlow.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/HandlerFlow.kt @@ -14,14 +14,10 @@ import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.onCompletion -class HandlerFlow<T>( - looper: Looper, - private val extractor: (Message) -> T -) : Handler(looper), Flow<T> { +class HandlerFlow<T>(looper: Looper, private val extractor: (Message) -> T) : + Handler(looper), Flow<T> { private val channel = Channel<T>(Channel.UNLIMITED) - private val flow = channel.consumeAsFlow().onCompletion { - removeCallbacksAndMessages(null) - } + private val flow = channel.consumeAsFlow().onCompletion { removeCallbacksAndMessages(null) } @InternalCoroutinesApi override suspend fun collect(collector: FlowCollector<T>) = flow.collect(collector) @@ -33,7 +29,8 @@ class HandlerFlow<T>( channel.trySendBlocking(extractedData) } catch (exception: Exception) { when (exception) { - is ClosedSendChannelException, is CancellationException -> { + is ClosedSendChannelException, + is CancellationException -> { Log.w("mullvad", "Received a message after HandlerFlow was closed", exception) removeCallbacksAndMessages(null) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Message.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Message.kt index 7758f6c926..cdad0a6b13 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Message.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Message.kt @@ -11,11 +11,12 @@ sealed class Message(private val messageId: Int) : Parcelable { protected abstract val messageKey: String val message: RawMessage - get() = RawMessage.obtain().also { message -> - message.what = messageId - message.data = Bundle() - message.data.putParcelable(messageKey, this) - } + get() = + RawMessage.obtain().also { message -> + message.what = messageId + message.data = Bundle() + message.data.putParcelable(messageKey, this) + } companion object { internal fun <T : Parcelable> fromMessage(message: RawMessage, key: String): T? { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt index 57dcd1bc98..56900e4440 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/Request.kt @@ -15,59 +15,41 @@ sealed class Request : Message.RequestMessage() { @Deprecated("Use SetDnsOptions") data class AddCustomDnsServer(val address: InetAddress) : Request() - @Parcelize - object Connect : Request() + @Parcelize object Connect : Request() - @Parcelize - object CreateAccount : Request() + @Parcelize object CreateAccount : Request() - @Parcelize - object Disconnect : Request() + @Parcelize object Disconnect : Request() - @Parcelize - data class ExcludeApp(val packageName: String) : Request() + @Parcelize data class ExcludeApp(val packageName: String) : Request() - @Parcelize - object FetchAccountExpiry : Request() + @Parcelize object FetchAccountExpiry : Request() - @Parcelize - object FetchAccountHistory : Request() + @Parcelize object FetchAccountHistory : Request() - @Parcelize - object FetchAuthToken : Request() + @Parcelize object FetchAuthToken : Request() - @Parcelize - data class IncludeApp(val packageName: String) : Request() + @Parcelize data class IncludeApp(val packageName: String) : Request() - @Parcelize - data class Login(val account: String?) : Request() + @Parcelize data class Login(val account: String?) : Request() - @Parcelize - object RefreshDeviceState : Request() + @Parcelize object RefreshDeviceState : Request() - @Parcelize - object GetDevice : Request() + @Parcelize object GetDevice : Request() - @Parcelize - data class GetDeviceList(val accountToken: String) : Request() + @Parcelize data class GetDeviceList(val accountToken: String) : Request() - @Parcelize - data class RemoveDevice(val accountToken: String, val deviceId: String) : Request() + @Parcelize data class RemoveDevice(val accountToken: String, val deviceId: String) : Request() - @Parcelize - object Logout : Request() + @Parcelize object Logout : Request() - @Parcelize - object PersistExcludedApps : Request() + @Parcelize object PersistExcludedApps : Request() - @Parcelize - object Reconnect : Request() + @Parcelize object Reconnect : Request() - @Parcelize - data class RegisterListener(val listener: Messenger) : Request() + @Parcelize data class RegisterListener(val listener: Messenger) : Request() - @Parcelize - object ClearAccountHistory : Request() + @Parcelize object ClearAccountHistory : Request() @Parcelize @Deprecated("Use SetDnsOptions") @@ -75,41 +57,30 @@ sealed class Request : Message.RequestMessage() { @Parcelize @Deprecated("Use SetDnsOptions") - data class ReplaceCustomDnsServer( - val oldAddress: InetAddress, - val newAddress: InetAddress - ) : Request() + data class ReplaceCustomDnsServer(val oldAddress: InetAddress, val newAddress: InetAddress) : + Request() - @Parcelize - data class SetAllowLan(val allow: Boolean) : Request() + @Parcelize data class SetAllowLan(val allow: Boolean) : Request() - @Parcelize - data class SetAutoConnect(val autoConnect: Boolean) : Request() + @Parcelize data class SetAutoConnect(val autoConnect: Boolean) : Request() @Parcelize @Deprecated("Use SetDnsOptions") data class SetEnableCustomDns(val enable: Boolean) : Request() - @Parcelize - data class SetEnableSplitTunneling(val enable: Boolean) : Request() + @Parcelize data class SetEnableSplitTunneling(val enable: Boolean) : Request() - @Parcelize - data class SetRelayLocation(val relayLocation: LocationConstraint?) : Request() + @Parcelize data class SetRelayLocation(val relayLocation: LocationConstraint?) : Request() - @Parcelize - data class SetWireGuardMtu(val mtu: Int?) : Request() + @Parcelize data class SetWireGuardMtu(val mtu: Int?) : Request() - @Parcelize - data class SubmitVoucher(val voucher: String) : Request() + @Parcelize data class SubmitVoucher(val voucher: String) : Request() - @Parcelize - data class UnregisterListener(val listenerId: Int) : Request() + @Parcelize data class UnregisterListener(val listenerId: Int) : Request() - @Parcelize - data class VpnPermissionResponse(val isGranted: Boolean) : Request() + @Parcelize data class VpnPermissionResponse(val isGranted: Boolean) : Request() - @Parcelize - data class SetDnsOptions(val dnsOptions: DnsOptions) : Request() + @Parcelize data class SetDnsOptions(val dnsOptions: DnsOptions) : Request() companion object { private const val MESSAGE_KEY = "request" diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt index 66ac88c91d..c58af82073 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ipc/ServiceConnection.kt @@ -46,23 +46,26 @@ class ServiceConnection(context: Context, scope: CoroutineScope) { Channel<ServiceResult.ConnectionState>(Channel.RENDEZVOUS) init { - val dispatcher = handler - .filterNotNull() - .dispatchTo { - listenerRegistrations = subscribeToState(Event.ListenerReady::class, scope) { - Pair(connection, listenerId) - } + val dispatcher = + handler.filterNotNull().dispatchTo { + listenerRegistrations = + subscribeToState(Event.ListenerReady::class, scope) { + Pair(connection, listenerId) + } - val tunnelStateEvents = subscribeToState( - Event.TunnelStateChange::class, - scope, - TunnelState.Disconnected - ) { tunnelState } + val tunnelStateEvents = + subscribeToState( + Event.TunnelStateChange::class, + scope, + TunnelState.Disconnected + ) { + tunnelState + } - tunnelState = tunnelStateEvents - .combine( - serviceConnectionStateChannel.consumeAsFlow() - ) { tunnelState, serviceConnectionState -> + tunnelState = + tunnelStateEvents.combine(serviceConnectionStateChannel.consumeAsFlow()) { + tunnelState, + serviceConnectionState -> tunnelState to serviceConnectionState } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt index 1a4ed323b9..f5137ebbb7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountAndDevice.kt @@ -3,8 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class AccountAndDevice( - val account_token: String, - val device: Device -) : Parcelable +@Parcelize data class AccountAndDevice(val account_token: String, val device: Device) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountCreationResult.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountCreationResult.kt index 23115b606d..4bb4c61384 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountCreationResult.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountCreationResult.kt @@ -4,9 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class AccountCreationResult : Parcelable { - @Parcelize - data class Success(val accountToken: String) : AccountCreationResult() + @Parcelize data class Success(val accountToken: String) : AccountCreationResult() - @Parcelize - object Failure : AccountCreationResult() + @Parcelize object Failure : AccountCreationResult() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt index b057308192..a91ce46148 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountExpiry.kt @@ -5,11 +5,9 @@ import kotlinx.parcelize.Parcelize import org.joda.time.DateTime sealed class AccountExpiry : Parcelable { - @Parcelize - data class Available(val expiryDateTime: DateTime) : AccountExpiry() + @Parcelize data class Available(val expiryDateTime: DateTime) : AccountExpiry() - @Parcelize - object Missing : AccountExpiry() + @Parcelize object Missing : AccountExpiry() fun date(): DateTime? { return (this as? Available)?.expiryDateTime diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt index 11e9b20604..008eb1ea7a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AccountHistory.kt @@ -4,11 +4,9 @@ import android.os.Parcelable import kotlinx.android.parcel.Parcelize sealed class AccountHistory : Parcelable { - @Parcelize - data class Available(val accountToken: String) : AccountHistory() + @Parcelize data class Available(val accountToken: String) : AccountHistory() - @Parcelize - object Missing : AccountHistory() + @Parcelize object Missing : AccountHistory() fun accountToken() = (this as? Available)?.accountToken } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt index cc1127d026..bbe99ce656 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/AppVersionInfo.kt @@ -4,7 +4,4 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class AppVersionInfo( - val supported: Boolean, - val suggestedUpgrade: String? -) : Parcelable +data class AppVersionInfo(val supported: Boolean, val suggestedUpgrade: String?) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt index 37b98298ab..c6dc2bb091 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Constraint.kt @@ -8,6 +8,5 @@ sealed class Constraint<T>() : Parcelable { @Suppress("PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY") class Any<T>() : Constraint<T>() - @Parcelize - data class Only<T : Parcelable>(val value: T) : Constraint<T>() + @Parcelize data class Only<T : Parcelable>(val value: T) : Constraint<T>() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomDnsOptions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomDnsOptions.kt index e9e8311c26..bbf029dd4d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomDnsOptions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/CustomDnsOptions.kt @@ -4,7 +4,4 @@ import android.os.Parcelable import java.net.InetAddress import kotlinx.parcelize.Parcelize -@Parcelize -data class CustomDnsOptions( - val addresses: ArrayList<InetAddress> -) : Parcelable +@Parcelize data class CustomDnsOptions(val addresses: ArrayList<InetAddress>) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Device.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Device.kt index b633920e7f..c497f296ba 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Device.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Device.kt @@ -14,8 +14,7 @@ data class Device( val created: String ) : Parcelable { - @IgnoredOnParcel - val creationDate by lazy { created.parseAsDateTime() } + @IgnoredOnParcel val creationDate by lazy { created.parseAsDateTime() } // Generated by Android Studio override fun equals(other: Any?): Boolean { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt index 3156b98833..741108612d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceEvent.kt @@ -4,7 +4,4 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class DeviceEvent( - val cause: DeviceEventCause, - val newState: DeviceState -) : Parcelable +data class DeviceEvent(val cause: DeviceEventCause, val newState: DeviceState) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceListEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceListEvent.kt index 1e0f78e985..7a2883617b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceListEvent.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceListEvent.kt @@ -7,8 +7,7 @@ sealed class DeviceListEvent : Parcelable { @Parcelize data class Available(val accountToken: String, val devices: List<Device>) : DeviceListEvent() - @Parcelize - object Error : DeviceListEvent() + @Parcelize object Error : DeviceListEvent() fun isAvailable(): Boolean { return (this is Available) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DevicePort.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DevicePort.kt index 1159fa1a47..e43eae3e6b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DevicePort.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DevicePort.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class DevicePort(val id: String) : Parcelable +@Parcelize data class DevicePort(val id: String) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt index e23f0857d1..440d03de55 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/DeviceState.kt @@ -4,20 +4,15 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class DeviceState : Parcelable { - @Parcelize - object Initial : DeviceState() + @Parcelize object Initial : DeviceState() - @Parcelize - object Unknown : DeviceState() + @Parcelize object Unknown : DeviceState() - @Parcelize - data class LoggedIn(val accountAndDevice: AccountAndDevice) : DeviceState() + @Parcelize data class LoggedIn(val accountAndDevice: AccountAndDevice) : DeviceState() - @Parcelize - object LoggedOut : DeviceState() + @Parcelize object LoggedOut : DeviceState() - @Parcelize - object Revoked : DeviceState() + @Parcelize object Revoked : DeviceState() fun isUnknown(): Boolean { return this is Unknown diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ListItemData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ListItemData.kt index 17aa7bae51..26ea17f083 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ListItemData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ListItemData.kt @@ -16,22 +16,17 @@ private constructor( val action: ItemAction? = null ) { - @Retention - @IntDef(DIVIDER, PLAIN, ACTION) - annotation class ItemType + @Retention @IntDef(DIVIDER, PLAIN, ACTION) annotation class ItemType class Builder(private val identifier: String) { var text: String? = null - @StringRes - var textRes: Int? = null + @StringRes var textRes: Int? = null - @DrawableRes - var iconRes: Int? = null + @DrawableRes var iconRes: Int? = null var isSelected: Boolean = false - @ItemType - var type: Int = 0 + @ItemType var type: Int = 0 var widget: WidgetState? = null var action: ItemAction? = null @@ -40,8 +35,14 @@ private constructor( throw IllegalArgumentException("ListItem should be configured with text") return ListItemData( - this.identifier, this.text, this.textRes, this.iconRes, - this.isSelected, this.type, this.widget, this.action + this.identifier, + this.text, + this.textRes, + this.iconRes, + this.isSelected, + this.type, + this.widget, + this.action ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt index 6734ff418e..2820a449b8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/LocationConstraint.kt @@ -19,11 +19,8 @@ sealed class LocationConstraint : Parcelable { } @Parcelize - data class Hostname( - val countryCode: String, - val cityCode: String, - val hostname: String - ) : LocationConstraint() { + data class Hostname(val countryCode: String, val cityCode: String, val hostname: String) : + LocationConstraint() { override val location: GeoIpLocation get() = GeoIpLocation(null, null, countryCode, cityCode, hostname) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/PublicKey.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/PublicKey.kt index 4ee6ad51df..169b6c3856 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/PublicKey.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/PublicKey.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class PublicKey(val key: ByteArray, val dateCreated: String) : Parcelable +@Parcelize data class PublicKey(val key: ByteArray, val dateCreated: String) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt index dbb74b129a..b1abdc3c75 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt @@ -4,11 +4,8 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class Relay( - val hostname: String, - val active: Boolean, - val endpointData: RelayEndpointData -) : Parcelable { +data class Relay(val hostname: String, val active: Boolean, val endpointData: RelayEndpointData) : + Parcelable { val isWireguardRelay get() = endpointData is RelayEndpointData.Wireguard } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraints.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraints.kt index cd36577dae..9b02e043ad 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraints.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayConstraints.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class RelayConstraints(val location: Constraint<LocationConstraint>) : Parcelable +@Parcelize data class RelayConstraints(val location: Constraint<LocationConstraint>) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt index ecc2d4d002..86b3f0fa35 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt @@ -4,14 +4,11 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class RelayEndpointData : Parcelable { - @Parcelize - object Openvpn : RelayEndpointData() + @Parcelize object Openvpn : RelayEndpointData() - @Parcelize - object Bridge : RelayEndpointData() + @Parcelize object Bridge : RelayEndpointData() @Parcelize - data class Wireguard( - val wireguardRelayEndpointData: WireguardRelayEndpointData - ) : RelayEndpointData() + data class Wireguard(val wireguardRelayEndpointData: WireguardRelayEndpointData) : + RelayEndpointData() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayList.kt index 08c84aad01..2d13dc9322 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayList.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayList.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class RelayList(val countries: ArrayList<RelayListCountry>) : Parcelable +@Parcelize data class RelayList(val countries: ArrayList<RelayListCountry>) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayListCity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayListCity.kt index 597cfb2758..2376609ced 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayListCity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayListCity.kt @@ -4,8 +4,5 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class RelayListCity( - val name: String, - val code: String, - val relays: ArrayList<Relay> -) : Parcelable +data class RelayListCity(val name: String, val code: String, val relays: ArrayList<Relay>) : + Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettings.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettings.kt index 6a247997db..7832a00e77 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettings.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelaySettings.kt @@ -4,9 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class RelaySettings : Parcelable { - @Parcelize - object CustomTunnelEndpoint : RelaySettings() + @Parcelize object CustomTunnelEndpoint : RelaySettings() - @Parcelize - class Normal(val relayConstraints: RelayConstraints) : RelaySettings() + @Parcelize class Normal(val relayConstraints: RelayConstraints) : RelaySettings() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt index 94499c032a..cc6e7db2bb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RemoveDeviceEvent.kt @@ -4,7 +4,5 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class RemoveDeviceEvent( - val accountToken: String, - val newDevices: ArrayList<Device> -) : Parcelable +data class RemoveDeviceEvent(val accountToken: String, val newDevices: ArrayList<Device>) : + Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt index b1d9f5be4c..e597797e5a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/ServiceResult.kt @@ -2,12 +2,10 @@ package net.mullvad.mullvadvpn.model import android.os.IBinder -data class ServiceResult( - val binder: IBinder? -) { +data class ServiceResult(val binder: IBinder?) { enum class ConnectionState { CONNECTED, - DISCONNECTED; + DISCONNECTED } val connectionState: ConnectionState diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt index 944a98b0b8..108fd32e04 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelOptions.kt @@ -4,7 +4,5 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class TunnelOptions( - val wireguard: WireguardTunnelOptions, - val dnsOptions: DnsOptions -) : Parcelable +data class TunnelOptions(val wireguard: WireguardTunnelOptions, val dnsOptions: DnsOptions) : + Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt index 6edfb2dc24..c513c77d8e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt @@ -8,28 +8,21 @@ import net.mullvad.talpid.tunnel.ErrorState import net.mullvad.talpid.tunnel.ErrorStateCause sealed class TunnelState() : Parcelable { - @Parcelize - object Disconnected : TunnelState(), Parcelable + @Parcelize object Disconnected : TunnelState(), Parcelable @Parcelize - class Connecting( - val endpoint: TunnelEndpoint?, - val location: GeoIpLocation? - ) : TunnelState(), Parcelable + class Connecting(val endpoint: TunnelEndpoint?, val location: GeoIpLocation?) : + TunnelState(), Parcelable @Parcelize - class Connected( - val endpoint: TunnelEndpoint, - val location: GeoIpLocation? - ) : TunnelState(), Parcelable + class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : + TunnelState(), Parcelable @Parcelize - class Disconnecting( - val actionAfterDisconnect: ActionAfterDisconnect - ) : TunnelState(), Parcelable + class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : + TunnelState(), Parcelable - @Parcelize - class Error(val errorState: ErrorState) : TunnelState(), Parcelable + @Parcelize class Error(val errorState: ErrorState) : TunnelState(), Parcelable fun isSecured(): Boolean { return when (this) { @@ -66,23 +59,24 @@ sealed class TunnelState() : Parcelable { } } - override fun toString(): String = when (this) { - is TunnelState.Disconnected -> DISCONNECTED - is TunnelState.Connecting -> CONNECTING - is TunnelState.Connected -> CONNECTED - is TunnelState.Disconnecting -> { - if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { - RECONNECTING - } else { - DISCONNECTING + override fun toString(): String = + when (this) { + is TunnelState.Disconnected -> DISCONNECTED + is TunnelState.Connecting -> CONNECTING + is TunnelState.Connected -> CONNECTED + is TunnelState.Disconnecting -> { + if (actionAfterDisconnect == ActionAfterDisconnect.Reconnect) { + RECONNECTING + } else { + DISCONNECTING + } } - } - is TunnelState.Error -> { - if (errorState.isBlocking) { - BLOCKING - } else { - ERROR + is TunnelState.Error -> { + if (errorState.isBlocking) { + BLOCKING + } else { + ERROR + } } } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmission.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmission.kt index bf96646516..efe05e2f5c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmission.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmission.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class VoucherSubmission(val timeAdded: Long, val newExpiry: String) : Parcelable +@Parcelize data class VoucherSubmission(val timeAdded: Long, val newExpiry: String) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmissionResult.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmissionResult.kt index b78957d5c0..4163b782d4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmissionResult.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/VoucherSubmissionResult.kt @@ -4,9 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize sealed class VoucherSubmissionResult : Parcelable { - @Parcelize - data class Ok(val submission: VoucherSubmission) : VoucherSubmissionResult() + @Parcelize data class Ok(val submission: VoucherSubmission) : VoucherSubmissionResult() - @Parcelize - data class Error(val error: VoucherSubmissionError) : VoucherSubmissionResult() + @Parcelize data class Error(val error: VoucherSubmissionError) : VoucherSubmissionResult() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt index b3ef17f98a..4a1930dd43 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt @@ -3,5 +3,4 @@ package net.mullvad.mullvadvpn.model import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -object WireguardRelayEndpointData : Parcelable +@Parcelize object WireguardRelayEndpointData : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt index 85a5ebc894..47c8c54a57 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardTunnelOptions.kt @@ -4,7 +4,4 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class WireguardTunnelOptions( - val mtu: Int?, - val quantumResistant: Boolean? -) : Parcelable +data class WireguardTunnelOptions(val mtu: Int?, val quantumResistant: Boolean?) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt index 080236cff9..7afb2249d2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/Relay.kt @@ -2,11 +2,8 @@ package net.mullvad.mullvadvpn.relaylist import net.mullvad.mullvadvpn.model.LocationConstraint -data class Relay( - val city: RelayCity, - override val name: String, - override val active: Boolean -) : RelayItem { +data class Relay(val city: RelayCity, override val name: String, override val active: Boolean) : + RelayItem { override val code = name override val type = RelayItemType.Relay override val location = LocationConstraint.Hostname(city.country.code, city.code, name) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt index 915e6ca181..b5aaed028a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt @@ -7,32 +7,33 @@ class RelayList { val countries: List<RelayCountry> constructor(model: net.mullvad.mullvadvpn.model.RelayList) { - var relayCountries = model.countries - .map { country -> - val cities = mutableListOf<RelayCity>() - val relayCountry = RelayCountry(country.name, country.code, false, cities) + var relayCountries = + model.countries + .map { country -> + val cities = mutableListOf<RelayCity>() + val relayCountry = RelayCountry(country.name, country.code, false, cities) - for (city in country.cities) { - val relays = mutableListOf<Relay>() - val relayCity = RelayCity(relayCountry, city.name, city.code, false, relays) + for (city in country.cities) { + val relays = mutableListOf<Relay>() + val relayCity = RelayCity(relayCountry, city.name, city.code, false, relays) - val validCityRelays = city.relays.filter { relay -> relay.isWireguardRelay } + val validCityRelays = city.relays.filter { relay -> relay.isWireguardRelay } - for (relay in validCityRelays) { - relays.add(Relay(relayCity, relay.hostname, relay.active)) - } - relays.sortWith(RelayNameComparator) + for (relay in validCityRelays) { + relays.add(Relay(relayCity, relay.hostname, relay.active)) + } + relays.sortWith(RelayNameComparator) - if (relays.isNotEmpty()) { - cities.add(relayCity) + if (relays.isNotEmpty()) { + cities.add(relayCity) + } } - } - cities.sortBy({ it.name }) - relayCountry - } - .filter { country -> country.cities.isNotEmpty() } - .toMutableList() + cities.sortBy({ it.name }) + relayCountry + } + .filter { country -> country.cities.isNotEmpty() } + .toMutableList() relayCountries.sortBy({ it.name }) @@ -53,9 +54,8 @@ class RelayList { return countries.find { country -> country.code == location.countryCode } } is LocationConstraint.City -> { - val country = countries.find { country -> - country.code == location.countryCode - } + val country = + countries.find { country -> country.code == location.countryCode } if (expand) { country?.expanded = true @@ -64,9 +64,8 @@ class RelayList { return country?.cities?.find { city -> city.code == location.cityCode } } is LocationConstraint.Hostname -> { - val country = countries.find { country -> - country.code == location.countryCode - } + val country = + countries.find { country -> country.code == location.countryCode } val city = country?.cities?.find { city -> city.code == location.cityCode } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparator.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparator.kt index 32f473b194..92a3c9c1d6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparator.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayNameComparator.kt @@ -4,19 +4,15 @@ internal object RelayNameComparator : Comparator<Relay> { override fun compare(o1: Relay, o2: Relay): Int { val partitions1 = o1.name.split(regex) val partitions2 = o2.name.split(regex) - return if (partitions1.size > partitions2.size) - partitions1 compareWith partitions2 - else - -(partitions2 compareWith partitions1) + return if (partitions1.size > partitions2.size) partitions1 compareWith partitions2 + else -(partitions2 compareWith partitions1) } private infix fun List<String>.compareWith(other: List<String>): Int { this.forEachIndexed { index, s -> - if (other.size <= index) - return 1 + if (other.size <= index) return 1 val partsCompareResult = compareStringOrInt(other[index], s) - if (partsCompareResult != 0) - return partsCompareResult + if (partsCompareResult != 0) return partsCompareResult } return 0 } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/AccountRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/AccountRepository.kt index afcd6a531b..106cf9a7d9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/AccountRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/AccountRepository.kt @@ -36,42 +36,37 @@ class AccountRepository( .onEach { _cachedCreatedAccount.value = (it as? AccountCreationResult.Success)?.accountToken } - .shareIn( + .shareIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed()) + + val accountExpiryState: StateFlow<AccountExpiry> = + serviceConnectionManager.connectionState + .flatMapReadyConnectionOrDefault(flowOf()) { state -> + state.container.accountDataSource.accountExpiry + } + .stateIn( CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed() + SharingStarted.WhileSubscribed(), + AccountExpiry.Missing ) - val accountExpiryState: StateFlow<AccountExpiry> = serviceConnectionManager.connectionState - .flatMapReadyConnectionOrDefault(flowOf()) { state -> - state.container.accountDataSource.accountExpiry - } - .stateIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed(), - AccountExpiry.Missing - ) - - val accountHistoryEvents: StateFlow<AccountHistory> = serviceConnectionManager.connectionState - .flatMapReadyConnectionOrDefault(flowOf()) { state -> - state.container.accountDataSource.accountHistory - } - .onStart { - fetchAccountHistory() - } - .stateIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed(), - AccountHistory.Missing - ) + val accountHistoryEvents: StateFlow<AccountHistory> = + serviceConnectionManager.connectionState + .flatMapReadyConnectionOrDefault(flowOf()) { state -> + state.container.accountDataSource.accountHistory + } + .onStart { fetchAccountHistory() } + .stateIn( + CoroutineScope(dispatcher), + SharingStarted.WhileSubscribed(), + AccountHistory.Missing + ) - val loginEvents: SharedFlow<Event.LoginEvent> = serviceConnectionManager.connectionState - .flatMapReadyConnectionOrDefault(flowOf()) { state -> - state.container.accountDataSource.loginEvents - } - .shareIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed() - ) + val loginEvents: SharedFlow<Event.LoginEvent> = + serviceConnectionManager.connectionState + .flatMapReadyConnectionOrDefault(flowOf()) { state -> + state.container.accountDataSource.loginEvents + } + .shareIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed()) fun createAccount() { serviceConnectionManager.accountDataSource()?.createAccount() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/DeviceRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/DeviceRepository.kt index 72e2d6ff31..2374f7dfe1 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/DeviceRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/DeviceRepository.kt @@ -29,22 +29,23 @@ class DeviceRepository( ) { private val cachedDeviceList = MutableStateFlow<DeviceList>(DeviceList.Unavailable) - val deviceState = serviceConnectionManager.connectionState - .flatMapLatest { state -> - if (state is ServiceConnectionState.ConnectedReady) { - state.container.deviceDataSource.deviceStateUpdates - } else { - flowOf(DeviceState.Unknown) + val deviceState = + serviceConnectionManager.connectionState + .flatMapLatest { state -> + if (state is ServiceConnectionState.ConnectedReady) { + state.container.deviceDataSource.deviceStateUpdates + } else { + flowOf(DeviceState.Unknown) + } } - } - .stateIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed(), - DeviceState.Initial - ) + .stateIn( + CoroutineScope(dispatcher), + SharingStarted.WhileSubscribed(), + DeviceState.Initial + ) - private val deviceListEvents = serviceConnectionManager.connectionState - .flatMapLatest { state -> + private val deviceListEvents = + serviceConnectionManager.connectionState.flatMapLatest { state -> if (state is ServiceConnectionState.ConnectedReady) { state.container.deviceDataSource.deviceListUpdates } else { @@ -52,23 +53,21 @@ class DeviceRepository( } } - val deviceList = deviceListEvents - .map { - if (it is DeviceListEvent.Available) { - DeviceList.Available(it.devices) - } else { - DeviceList.Error + val deviceList = + deviceListEvents + .map { + if (it is DeviceListEvent.Available) { + DeviceList.Available(it.devices) + } else { + DeviceList.Error + } } - } - .onStart { - if (cachedDeviceList.value is DeviceList.Available) { - emit(cachedDeviceList.value) + .onStart { + if (cachedDeviceList.value is DeviceList.Available) { + emit(cachedDeviceList.value) + } } - } - .shareIn( - CoroutineScope(Dispatchers.IO), - SharingStarted.WhileSubscribed() - ) + .shareIn(CoroutineScope(Dispatchers.IO), SharingStarted.WhileSubscribed()) val deviceRemovalEvent: SharedFlow<Event.DeviceRemovalEvent> = serviceConnectionManager.connectionState @@ -79,10 +78,7 @@ class DeviceRepository( emptyFlow() } } - .shareIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed() - ) + .shareIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed()) fun refreshDeviceState() { serviceConnectionManager.deviceDataSource()?.refreshDevice() @@ -121,13 +117,12 @@ class DeviceRepository( clearCache() } - val result = withTimeoutOrNull(timeoutMillis) { - deviceListEvents - .onStart { - refreshDeviceList(accountToken) - } - .firstOrNull() ?: DeviceListEvent.Error - } ?: DeviceListEvent.Error + val result = + withTimeoutOrNull(timeoutMillis) { + deviceListEvents.onStart { refreshDeviceList(accountToken) }.firstOrNull() + ?: DeviceListEvent.Error + } + ?: DeviceListEvent.Error if (shouldOverrideCache) { updateCache(result, accountToken) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt index 6096136688..db1ad220e3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/PrivacyDisclaimerRepository.kt @@ -4,9 +4,7 @@ import android.content.SharedPreferences private const val IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY = "is_privacy_disclosure_accepted" -class PrivacyDisclaimerRepository( - private val sharedPreferences: SharedPreferences -) { +class PrivacyDisclaimerRepository(private val sharedPreferences: SharedPreferences) { fun hasAcceptedPrivacyDisclosure(): Boolean { return sharedPreferences.getBoolean(IS_PRIVACY_DISCLOSURE_ACCEPTED_KEY, false) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt index 59d42cf476..6eef99ecce 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/repository/SettingsRepository.kt @@ -24,28 +24,25 @@ class SettingsRepository( private val serviceConnectionManager: ServiceConnectionManager, dispatcher: CoroutineDispatcher = Dispatchers.IO ) { - val settingsUpdates: StateFlow<Settings?> = serviceConnectionManager.connectionState - .flatMapReadyConnectionOrDefault(flowOf()) { state -> - callbackFlowFromNotifier(state.container.settingsListener.settingsNotifier) - } - .onStart { serviceConnectionManager.settingsListener()?.settingsNotifier?.latestEvent } - .stateIn( - CoroutineScope(dispatcher), - SharingStarted.WhileSubscribed(), - null - ) + val settingsUpdates: StateFlow<Settings?> = + serviceConnectionManager.connectionState + .flatMapReadyConnectionOrDefault(flowOf()) { state -> + callbackFlowFromNotifier(state.container.settingsListener.settingsNotifier) + } + .onStart { serviceConnectionManager.settingsListener()?.settingsNotifier?.latestEvent } + .stateIn(CoroutineScope(dispatcher), SharingStarted.WhileSubscribed(), null) - fun setDnsOptions( - isCustomDnsEnabled: Boolean, - dnsList: List<InetAddress> - ) { - serviceConnectionManager.customDns()?.setDnsOptions( - dnsOptions = DnsOptions( - state = if (isCustomDnsEnabled) DnsState.Custom else DnsState.Default, - customOptions = CustomDnsOptions(ArrayList(dnsList)), - defaultOptions = DefaultDnsOptions() + fun setDnsOptions(isCustomDnsEnabled: Boolean, dnsList: List<InetAddress>) { + serviceConnectionManager + .customDns() + ?.setDnsOptions( + dnsOptions = + DnsOptions( + state = if (isCustomDnsEnabled) DnsState.Custom else DnsState.Default, + customOptions = CustomDnsOptions(ArrayList(dnsList)), + defaultOptions = DefaultDnsOptions() + ) ) - ) } fun isLocalNetworkSharingEnabled(): Boolean { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt index 7175765e9f..fcab12c6c9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/DaemonInstance.kt @@ -13,9 +13,7 @@ import kotlinx.coroutines.channels.trySendBlocking import net.mullvad.mullvadvpn.lib.endpoint.ApiEndpointConfiguration import net.mullvad.mullvadvpn.util.Intermittent -class DaemonInstance( - private val vpnService: MullvadVpnService -) { +class DaemonInstance(private val vpnService: MullvadVpnService) { sealed class Command { data class Start(val apiEndpointConfiguration: ApiEndpointConfiguration) : Command() object Stop : Command() @@ -23,9 +21,8 @@ class DaemonInstance( private val commandChannel = spawnActor() - private var daemon by observable<MullvadDaemon?>(null) { _, oldInstance, _ -> - oldInstance?.onDestroy() - } + private var daemon by + observable<MullvadDaemon?>(null) { _, oldInstance, _ -> oldInstance?.onDestroy() } val intermittentDaemon = Intermittent<MullvadDaemon>() @@ -42,16 +39,17 @@ class DaemonInstance( intermittentDaemon.onDestroy() } - private fun spawnActor() = GlobalScope.actor(Dispatchers.Default, Channel.UNLIMITED) { - var isRunning = true + private fun spawnActor() = + GlobalScope.actor(Dispatchers.Default, Channel.UNLIMITED) { + var isRunning = true - while (isRunning) { - val startCommand = waitForCommand(channel, Command.Start::class) ?: break - startDaemon(startCommand.apiEndpointConfiguration) - isRunning = waitForCommand(channel, Command.Stop::class) is Command.Stop - stopDaemon() + while (isRunning) { + val startCommand = waitForCommand(channel, Command.Start::class) ?: break + startDaemon(startCommand.apiEndpointConfiguration) + isRunning = waitForCommand(channel, Command.Stop::class) is Command.Stop + stopDaemon() + } } - } private suspend fun <T : Command> waitForCommand( channel: ReceiveChannel<Command>, @@ -68,15 +66,14 @@ class DaemonInstance( } } - private suspend fun startDaemon( - apiEndpointConfiguration: ApiEndpointConfiguration - ) { - val newDaemon = MullvadDaemon(vpnService, apiEndpointConfiguration).apply { - onDaemonStopped = { - intermittentDaemon.spawnUpdate(null) - daemon = null + private suspend fun startDaemon(apiEndpointConfiguration: ApiEndpointConfiguration) { + val newDaemon = + MullvadDaemon(vpnService, apiEndpointConfiguration).apply { + onDaemonStopped = { + intermittentDaemon.spawnUpdate(null) + daemon = null + } } - } daemon = newDaemon intermittentDaemon.update(newDaemon) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt index f7a179a35e..8fb7108619 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/ForegroundNotificationManager.kt @@ -32,9 +32,8 @@ class ForegroundNotificationManager( private val tunnelStateNotification = TunnelStateNotification(service) - private var loggedIn by observable(false) { _, _, _ -> - updater.trySendBlocking(UpdaterMessage.UpdateAction()) - } + private var loggedIn by + observable(false) { _, _, _ -> updater.trySendBlocking(UpdaterMessage.UpdateAction()) } private val tunnelState get() = connectionProxy.onStateChange.latestEvent @@ -45,9 +44,10 @@ class ForegroundNotificationManager( var onForeground = false private set - var lockedToForeground by observable(false) { _, _, _ -> - updater.trySendBlocking(UpdaterMessage.UpdateNotification()) - } + var lockedToForeground by + observable(false) { _, _, _ -> + updater.trySendBlocking(UpdaterMessage.UpdateNotification()) + } init { connectionProxy.onStateChange.subscribe(this) { newState -> @@ -56,13 +56,10 @@ class ForegroundNotificationManager( intermittentDaemon.registerListener(this) { daemon -> jobTracker.newBackgroundJob("notificationLoggedInJob") { - daemon?.deviceStateUpdates - ?.onStart { - emit(daemon.getAndEmitDeviceState()) - } - ?.collect { deviceState -> - loggedIn = deviceState is DeviceState.LoggedIn - } + daemon + ?.deviceStateUpdates + ?.onStart { emit(daemon.getAndEmitDeviceState()) } + ?.collect { deviceState -> loggedIn = deviceState is DeviceState.LoggedIn } } } @@ -76,21 +73,19 @@ class ForegroundNotificationManager( updater.close() } - private fun runUpdater() = GlobalScope.actor<UpdaterMessage>( - Dispatchers.Main, - Channel.UNLIMITED - ) { - for (message in channel) { - when (message) { - is UpdaterMessage.UpdateNotification -> updateNotification() - is UpdaterMessage.UpdateAction -> updateNotificationAction() - is UpdaterMessage.NewTunnelState -> { - tunnelStateNotification.tunnelState = message.newState - updateNotification() + private fun runUpdater() = + GlobalScope.actor<UpdaterMessage>(Dispatchers.Main, Channel.UNLIMITED) { + for (message in channel) { + when (message) { + is UpdaterMessage.UpdateNotification -> updateNotification() + is UpdaterMessage.UpdateAction -> updateNotificationAction() + is UpdaterMessage.NewTunnelState -> { + tunnelStateNotification.tunnelState = message.newState + updateNotification() + } } } } - } fun showOnForeground() { service.startForeground( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt index e26ae28697..96630a2cec 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadTileService.kt @@ -45,15 +45,17 @@ class MullvadTileService : TileService() { delay(unlockCheckDelayMillis) } return@withTimeoutOrNull true - } ?: false + } + ?: false } unlockAndRun { runBlocking { - val isUnlockStatusPropagated = isUnlockStatusPropagatedWithinTimeout( - unlockTimeoutMillis = 1000L, - unlockCheckDelayMillis = 100L - ) + val isUnlockStatusPropagated = + isUnlockStatusPropagatedWithinTimeout( + unlockTimeoutMillis = 1000L, + unlockCheckDelayMillis = 100L + ) if (isUnlockStatusPropagated) { toggleTunnel() @@ -73,13 +75,15 @@ class MullvadTileService : TileService() { } private fun toggleTunnel() { - val intent = Intent(this, MullvadVpnService::class.java).apply { - action = if (qsTile.state == Tile.STATE_INACTIVE) { - MullvadVpnService.KEY_CONNECT_ACTION - } else { - MullvadVpnService.KEY_DISCONNECT_ACTION + val intent = + Intent(this, MullvadVpnService::class.java).apply { + action = + if (qsTile.state == Tile.STATE_INACTIVE) { + MullvadVpnService.KEY_CONNECT_ACTION + } else { + MullvadVpnService.KEY_DISCONNECT_ACTION + } } - } // Always start as foreground in case tile is out-of-sync. startForegroundService(intent) @@ -91,9 +95,7 @@ class MullvadTileService : TileService() { .tunnelState .debounce(300L) .map { (tunnelState, connectionState) -> mapToTileState(tunnelState, connectionState) } - .collect { - updateTileState(it) - } + .collect { updateTileState(it) } } private fun mapToTileState( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt index c35125c326..0058b09e4f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/MullvadVpnService.kt @@ -62,11 +62,10 @@ class MullvadVpnService : TalpidVpnService() { private lateinit var keyguardManager: KeyguardManager private lateinit var notificationManager: ForegroundNotificationManager - private var pendingAction by observable<PendingAction?>(null) { _, _, _ -> - endpoint.settingsListener.settings?.let { settings -> - handlePendingAction(settings) + private var pendingAction by + observable<PendingAction?>(null) { _, _, _ -> + endpoint.settingsListener.settings?.let { settings -> handlePendingAction(settings) } } - } private var apiEndpointConfiguration: ApiEndpointConfiguration = DefaultApiEndpointConfiguration() @@ -80,12 +79,13 @@ class MullvadVpnService : TalpidVpnService() { daemonInstance = DaemonInstance(this) keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - endpoint = ServiceEndpoint( - Looper.getMainLooper(), - daemonInstance.intermittentDaemon, - connectivityListener, - this - ) + endpoint = + ServiceEndpoint( + Looper.getMainLooper(), + daemonInstance.intermittentDaemon, + connectivityListener, + this + ) endpoint.splitTunneling.onChange.subscribe(this@MullvadVpnService) { excludedApps -> disallowedApps = excludedApps @@ -93,32 +93,25 @@ class MullvadVpnService : TalpidVpnService() { connectionProxy.reconnect() } - notificationManager = ForegroundNotificationManager( - this, - connectionProxy, - daemonInstance.intermittentDaemon - ) + notificationManager = + ForegroundNotificationManager(this, connectionProxy, daemonInstance.intermittentDaemon) - accountExpiryNotification = AccountExpiryNotification( - this, - daemonInstance.intermittentDaemon, - endpoint.accountCache - ) + accountExpiryNotification = + AccountExpiryNotification( + this, + daemonInstance.intermittentDaemon, + endpoint.accountCache + ) // Remove any leftover tunnel state persistence data - getSharedPreferences("tunnel_state", MODE_PRIVATE) - .edit() - .clear() - .commit() + getSharedPreferences("tunnel_state", MODE_PRIVATE).edit().clear().commit() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d(TAG, "Starting service") if (BuildConfig.DEBUG) { - intent?.getApiEndpointConfigurationExtras()?.let { - apiEndpointConfiguration = it - } + intent?.getApiEndpointConfigurationExtras()?.let { apiEndpointConfiguration = it } } daemonInstance.apply { @@ -219,17 +212,18 @@ class MullvadVpnService : TalpidVpnService() { } } - private fun setUpDaemon(daemon: MullvadDaemon) = GlobalScope.launch(Dispatchers.Main) { - if (state != State.Stopped) { - val settings = daemon.getSettings() + private fun setUpDaemon(daemon: MullvadDaemon) = + GlobalScope.launch(Dispatchers.Main) { + if (state != State.Stopped) { + val settings = daemon.getSettings() - if (settings != null) { - handlePendingAction(settings) - } else { - restart() + if (settings != null) { + handlePendingAction(settings) + } else { + restart() + } } } - } private fun stop() { Log.d(TAG, "Stopping service") @@ -270,10 +264,11 @@ class MullvadVpnService : TalpidVpnService() { } private fun openUi() { - val intent = Intent(this, MainActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - } + val intent = + Intent(this, MainActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + } startActivity(intent) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt index b363dbbe5f..e33e3d028f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AccountCache.kt @@ -48,9 +48,12 @@ class AccountCache(private val endpoint: ServiceEndpoint) { init { jobTracker.newBackgroundJob("autoFetchAccountExpiry") { daemon.await().deviceStateUpdates.collect { deviceState -> - accountExpiry = deviceState.token() - .also { cachedAccountToken = it } - ?.let { fetchAccountExpiry(it) } ?: AccountExpiry.Missing + accountExpiry = + deviceState + .token() + .also { cachedAccountToken = it } + ?.let { fetchAccountExpiry(it) } + ?: AccountExpiry.Missing } } @@ -58,9 +61,7 @@ class AccountCache(private val endpoint: ServiceEndpoint) { endpoint.sendEvent(Event.AccountHistoryEvent(history)) } - onAccountExpiryChange.subscribe(this) { - endpoint.sendEvent(Event.AccountExpiryEvent(it)) - } + onAccountExpiryChange.subscribe(this) { endpoint.sendEvent(Event.AccountExpiryEvent(it)) } endpoint.dispatcher.apply { registerHandler(Request.CreateAccount::class) { _ -> @@ -79,8 +80,8 @@ class AccountCache(private val endpoint: ServiceEndpoint) { registerHandler(Request.FetchAccountExpiry::class) { _ -> jobTracker.newBackgroundJob("fetchAccountExpiry") { - accountExpiry = cachedAccountToken - ?.let { fetchAccountExpiry(it) } ?: AccountExpiry.Missing + accountExpiry = + cachedAccountToken?.let { fetchAccountExpiry(it) } ?: AccountExpiry.Missing } } @@ -91,9 +92,7 @@ class AccountCache(private val endpoint: ServiceEndpoint) { } registerHandler(Request.ClearAccountHistory::class) { _ -> - jobTracker.newBackgroundJob("clearAccountHistory") { - clearAccountHistory() - } + jobTracker.newBackgroundJob("clearAccountHistory") { clearAccountHistory() } } } } @@ -107,19 +106,20 @@ class AccountCache(private val endpoint: ServiceEndpoint) { commandChannel.close() } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - for (command in channel) { - when (command) { - is Command.CreateAccount -> doCreateAccount() - is Command.Login -> doLogin(command.account) - is Command.Logout -> doLogout() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (command in channel) { + when (command) { + is Command.CreateAccount -> doCreateAccount() + is Command.Login -> doLogin(command.account) + is Command.Logout -> doLogout() + } } + } catch (exception: ClosedReceiveChannelException) { + // Command channel was closed, stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Command channel was closed, stop the actor } - } private suspend fun clearAccountHistory() { daemon.await().clearAccountHistory() @@ -127,10 +127,10 @@ class AccountCache(private val endpoint: ServiceEndpoint) { } private suspend fun doCreateAccount() { - daemon.await().createNewAccount() - .also { newAccountToken -> - cachedCreatedAccountToken = newAccountToken - } + daemon + .await() + .createNewAccount() + .also { newAccountToken -> cachedCreatedAccountToken = newAccountToken } .let { newAccountToken -> if (newAccountToken != null) { AccountCreationResult.Success(newAccountToken) @@ -138,9 +138,7 @@ class AccountCache(private val endpoint: ServiceEndpoint) { AccountCreationResult.Failure } } - .also { result -> - endpoint.sendEvent(Event.AccountCreationEvent(result)) - } + .also { result -> endpoint.sendEvent(Event.AccountCreationEvent(result)) } } private suspend fun doLogin(account: String) { @@ -169,7 +167,8 @@ class AccountCache(private val endpoint: ServiceEndpoint) { if (result is GetAccountDataResult.Ok) { result.accountData.expiry.parseAsDateTime()?.let { parsedDateTime -> AccountExpiry.Available(parsedDateTime) - } ?: AccountExpiry.Missing + } + ?: AccountExpiry.Missing } else { AccountExpiry.Missing } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AppVersionInfoCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AppVersionInfoCache.kt index 0c95293ae7..596ad21a3f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AppVersionInfoCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AppVersionInfoCache.kt @@ -8,14 +8,16 @@ import net.mullvad.mullvadvpn.service.MullvadDaemon class AppVersionInfoCache(endpoint: ServiceEndpoint) { private val daemon = endpoint.intermittentDaemon - var appVersionInfo by observable<AppVersionInfo?>(null) { _, _, info -> - endpoint.sendEvent(Event.AppVersionInfo(info)) - } + var appVersionInfo by + observable<AppVersionInfo?>(null) { _, _, info -> + endpoint.sendEvent(Event.AppVersionInfo(info)) + } private set - var currentVersion by observable<String?>(null) { _, _, version -> - endpoint.sendEvent(Event.CurrentVersion(version)) - } + var currentVersion by + observable<String?>(null) { _, _, version -> + endpoint.sendEvent(Event.CurrentVersion(version)) + } private set init { @@ -40,9 +42,7 @@ class AppVersionInfoCache(endpoint: ServiceEndpoint) { private fun registerVersionInfoListener(daemon: MullvadDaemon) { daemon.onAppVersionInfoChange = { newAppVersionInfo -> - synchronized(this@AppVersionInfoCache) { - appVersionInfo = newAppVersionInfo - } + synchronized(this@AppVersionInfoCache) { appVersionInfo = newAppVersionInfo } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt index add7a7ed06..49c4d1faac 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/AuthTokenCache.kt @@ -20,9 +20,8 @@ class AuthTokenCache(endpoint: ServiceEndpoint) { private val daemon = endpoint.intermittentDaemon private val requestQueue = spawnActor() - var authToken by observable<String?>(null) { _, _, token -> - endpoint.sendEvent(Event.AuthToken(token)) - } + var authToken by + observable<String?>(null) { _, _, token -> endpoint.sendEvent(Event.AuthToken(token)) } private set init { @@ -35,15 +34,16 @@ class AuthTokenCache(endpoint: ServiceEndpoint) { requestQueue.close() } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - for (command in channel) { - when (command) { - Command.Fetch -> authToken = daemon.await().getWwwAuthToken() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (command in channel) { + when (command) { + Command.Fetch -> authToken = daemon.await().getWwwAuthToken() + } } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Closed sender, so stop the actor } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt index 03808dcb94..fcdcece119 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ConnectionProxy.kt @@ -63,22 +63,23 @@ class ConnectionProxy(val vpnPermission: VpnPermission, endpoint: ServiceEndpoin daemon.unregisterListener(this) } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - while (true) { - val command = channel.receive() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + while (true) { + val command = channel.receive() - when (command) { - Command.CONNECT -> { - vpnPermission.request() - daemon.await().connect() + when (command) { + Command.CONNECT -> { + vpnPermission.request() + daemon.await().connect() + } + Command.RECONNECT -> daemon.await().reconnect() + Command.DISCONNECT -> daemon.await().disconnect() } - Command.RECONNECT -> daemon.await().reconnect() - Command.DISCONNECT -> daemon.await().disconnect() } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Closed sender, so stop the actor } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/CustomDns.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/CustomDns.kt index 62943ee0cb..5a24bea643 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/CustomDns.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/CustomDns.kt @@ -15,14 +15,11 @@ import net.mullvad.mullvadvpn.model.DnsState class CustomDns(private val endpoint: ServiceEndpoint) { private sealed class Command { - @Deprecated("Use SetDnsOptions") - class AddDnsServer(val server: InetAddress) : Command() - @Deprecated("Use SetDnsOptions") - class RemoveDnsServer(val server: InetAddress) : Command() + @Deprecated("Use SetDnsOptions") class AddDnsServer(val server: InetAddress) : Command() + @Deprecated("Use SetDnsOptions") class RemoveDnsServer(val server: InetAddress) : Command() @Deprecated("Use SetDnsOptions") class ReplaceDnsServer(val oldServer: InetAddress, val newServer: InetAddress) : Command() - @Deprecated("Use SetDnsOptions") - class SetEnabled(val enabled: Boolean) : Command() + @Deprecated("Use SetDnsOptions") class SetEnabled(val enabled: Boolean) : Command() class SetDnsOptions(val dnsOptions: DnsOptions) : Command() } @@ -74,25 +71,26 @@ class CustomDns(private val endpoint: ServiceEndpoint) { commandChannel.close() } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - while (true) { - val command = channel.receive() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + while (true) { + val command = channel.receive() - when (command) { - is Command.AddDnsServer -> doAddDnsServer(command.server) - is Command.RemoveDnsServer -> doRemoveDnsServer(command.server) - is Command.ReplaceDnsServer -> { - doReplaceDnsServer(command.oldServer, command.newServer) + when (command) { + is Command.AddDnsServer -> doAddDnsServer(command.server) + is Command.RemoveDnsServer -> doRemoveDnsServer(command.server) + is Command.ReplaceDnsServer -> { + doReplaceDnsServer(command.oldServer, command.newServer) + } + is Command.SetEnabled -> changeDnsOptions(command.enabled) + is Command.SetDnsOptions -> setDnsOptions(command.dnsOptions) } - is Command.SetEnabled -> changeDnsOptions(command.enabled) - is Command.SetDnsOptions -> setDnsOptions(command.dnsOptions) } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Closed sender, so stop the actor } - } private suspend fun doAddDnsServer(server: InetAddress) { if (!dnsServers.contains(server)) { @@ -120,11 +118,12 @@ class CustomDns(private val endpoint: ServiceEndpoint) { } private suspend fun changeDnsOptions(enable: Boolean) { - val options = DnsOptions( - state = if (enable) DnsState.Custom else DnsState.Default, - customOptions = CustomDnsOptions(dnsServers), - defaultOptions = DefaultDnsOptions() - ) + val options = + DnsOptions( + state = if (enable) DnsState.Custom else DnsState.Default, + customOptions = CustomDnsOptions(dnsServers), + defaultOptions = DefaultDnsOptions() + ) daemon.await().setDnsOptions(options) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt index 9bdf30de21..8f49ba2763 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/DaemonDeviceDataSource.kt @@ -6,9 +6,7 @@ import net.mullvad.mullvadvpn.ipc.Request import net.mullvad.mullvadvpn.service.MullvadDaemon import net.mullvad.mullvadvpn.util.JobTracker -class DaemonDeviceDataSource( - val endpoint: ServiceEndpoint -) { +class DaemonDeviceDataSource(val endpoint: ServiceEndpoint) { private val tracker = JobTracker() init { @@ -35,15 +33,11 @@ class DaemonDeviceDataSource( } endpoint.dispatcher.registerHandler(Request.GetDevice::class) { - tracker.newBackgroundJob("getDeviceJob") { - daemon.getAndEmitDeviceState() - } + tracker.newBackgroundJob("getDeviceJob") { daemon.getAndEmitDeviceState() } } endpoint.dispatcher.registerHandler(Request.RefreshDeviceState::class) { - tracker.newBackgroundJob("refreshDeviceJob") { - daemon.refreshDevice() - } + tracker.newBackgroundJob("refreshDeviceJob") { daemon.refreshDevice() } } endpoint.dispatcher.registerHandler(Request.RemoveDevice::class) { request -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt index 4fbc1fab4b..7cc43925d7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/LocationInfoCache.kt @@ -29,11 +29,12 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) { } } - private val fetchRetryDelays = ExponentialBackoff().apply { - scale = 50 - cap = 30 /* min */ * 60 /* s */ * 1000 /* ms */ - count = 17 // ceil(log2(cap / scale) + 1) - } + private val fetchRetryDelays = + ExponentialBackoff().apply { + scale = 50 + cap = 30 /* min */ * 60 /* s */ * 1000 /* ms */ + count = 17 // ceil(log2(cap / scale) + 1) + } private val fetchRequestChannel = runFetcher() @@ -43,36 +44,34 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) { private var lastKnownRealLocation: GeoIpLocation? = null private var selectedRelayLocation: GeoIpLocation? = null - var location: GeoIpLocation? by observable(null) { _, _, newLocation -> - endpoint.sendEvent(Event.NewLocation(newLocation)) - } + var location: GeoIpLocation? by + observable(null) { _, _, newLocation -> endpoint.sendEvent(Event.NewLocation(newLocation)) } - var state by observable<TunnelState>(TunnelState.Disconnected) { _, _, newState -> - when (newState) { - is TunnelState.Disconnected -> { - location = lastKnownRealLocation - fetchRequestChannel.trySendBlocking(RequestFetch.ForRealLocation) - } - is TunnelState.Connecting -> location = newState.location - is TunnelState.Connected -> { - location = newState.location - fetchRequestChannel.trySendBlocking(RequestFetch.ForRelayLocation) - } - is TunnelState.Disconnecting -> { - when (newState.actionAfterDisconnect) { - ActionAfterDisconnect.Nothing -> location = lastKnownRealLocation - ActionAfterDisconnect.Block -> location = null - ActionAfterDisconnect.Reconnect -> location = selectedRelayLocation + var state by + observable<TunnelState>(TunnelState.Disconnected) { _, _, newState -> + when (newState) { + is TunnelState.Disconnected -> { + location = lastKnownRealLocation + fetchRequestChannel.trySendBlocking(RequestFetch.ForRealLocation) } + is TunnelState.Connecting -> location = newState.location + is TunnelState.Connected -> { + location = newState.location + fetchRequestChannel.trySendBlocking(RequestFetch.ForRelayLocation) + } + is TunnelState.Disconnecting -> { + when (newState.actionAfterDisconnect) { + ActionAfterDisconnect.Nothing -> location = lastKnownRealLocation + ActionAfterDisconnect.Block -> location = null + ActionAfterDisconnect.Reconnect -> location = selectedRelayLocation + } + } + is TunnelState.Error -> location = null } - is TunnelState.Error -> location = null } - } init { - endpoint.connectionProxy.onStateChange.subscribe(this) { newState -> - state = newState - } + endpoint.connectionProxy.onStateChange.subscribe(this) { newState -> state = newState } endpoint.connectivityListener.connectivityNotifier.subscribe(this) { isConnected -> if (isConnected && state is TunnelState.Disconnected) { @@ -91,18 +90,16 @@ class LocationInfoCache(private val endpoint: ServiceEndpoint) { fetchRequestChannel.close() } - private fun runFetcher() = GlobalScope.actor<RequestFetch>( - Dispatchers.Default, - Channel.CONFLATED - ) { - try { - fetcherLoop(channel) - } catch (exception: ClosedReceiveChannelException) { + private fun runFetcher() = + GlobalScope.actor<RequestFetch>(Dispatchers.Default, Channel.CONFLATED) { + try { + fetcherLoop(channel) + } catch (exception: ClosedReceiveChannelException) {} } - } private suspend fun fetcherLoop(channel: ReceiveChannel<RequestFetch>) { - channel.receiveAsFlow() + channel + .receiveAsFlow() .flatMapLatest(::fetchCurrentLocation) .collect(::handleFetchedLocation) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt index 1f59de4074..7515bad230 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/RelayListListener.kt @@ -26,13 +26,15 @@ class RelayListListener(endpoint: ServiceEndpoint) { private val commandChannel = spawnActor() private val daemon = endpoint.intermittentDaemon - private var selectedRelayLocation by observable<LocationConstraint?>(null) { _, _, _ -> - commandChannel.trySendBlocking(Command.SetRelayLocation) - } + private var selectedRelayLocation by + observable<LocationConstraint?>(null) { _, _, _ -> + commandChannel.trySendBlocking(Command.SetRelayLocation) + } - var relayList by observable<RelayList?>(null) { _, _, relays -> - endpoint.sendEvent(Event.NewRelayList(relays)) - } + var relayList by + observable<RelayList?>(null) { _, _, relays -> + endpoint.sendEvent(Event.NewRelayList(relays)) + } private set init { @@ -54,9 +56,7 @@ class RelayListListener(endpoint: ServiceEndpoint) { } private fun setUpListener(daemon: MullvadDaemon) { - daemon.onRelayListChange = { relayLocations -> - relayList = relayLocations - } + daemon.onRelayListChange = { relayLocations -> relayList = relayLocations } } private fun fetchInitialRelayList(daemon: MullvadDaemon) { @@ -67,22 +67,22 @@ class RelayListListener(endpoint: ServiceEndpoint) { } } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.CONFLATED) { - try { - for (command in channel) { - when (command) { - Command.SetRelayLocation -> updateRelayConstraints() + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.CONFLATED) { + try { + for (command in channel) { + when (command) { + Command.SetRelayLocation -> updateRelayConstraints() + } } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Closed sender, so stop the actor } - } private suspend fun updateRelayConstraints() { - val constraint: Constraint<LocationConstraint> = selectedRelayLocation?.let { location -> - Constraint.Only(location) - } ?: Constraint.Any() + val constraint: Constraint<LocationConstraint> = + selectedRelayLocation?.let { location -> Constraint.Only(location) } ?: Constraint.Any() val update = RelaySettingsUpdate.Normal(RelayConstraintsUpdate(constraint)) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt index 4230aa14f1..eb3bf3627a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/ServiceEndpoint.kt @@ -37,9 +37,7 @@ class ServiceEndpoint( private val listeners = mutableMapOf<Int, Messenger>() private val commands: SendChannel<Command> = startRegistrator() - internal val dispatcher = DispatchingHandler(looper) { message -> - Request.fromMessage(message) - } + internal val dispatcher = DispatchingHandler(looper) { message -> Request.fromMessage(message) } private var listenerIdCounter = 0 @@ -95,11 +93,7 @@ class ServiceEndpoint( val deadListeners = mutableSetOf<Int>() for ((id, listener) in listeners) { - if (!listener.trySendEvent( - event, - SHOULD_LOG_DEAD_OBJECT_EXCEPTION - ) - ) { + if (!listener.trySendEvent(event, SHOULD_LOG_DEAD_OBJECT_EXCEPTION)) { deadListeners.add(id) } } @@ -107,25 +101,23 @@ class ServiceEndpoint( } } - private fun startRegistrator() = GlobalScope.actor<Command>( - Dispatchers.Default, - Channel.UNLIMITED - ) { - try { - for (command in channel) { - when (command) { - is Command.RegisterListener -> { - intermittentDaemon.await() + private fun startRegistrator() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (command in channel) { + when (command) { + is Command.RegisterListener -> { + intermittentDaemon.await() - registerListener(command.listener) + registerListener(command.listener) + } + is Command.UnregisterListener -> unregisterListener(command.listenerId) } - is Command.UnregisterListener -> unregisterListener(command.listenerId) } + } catch (exception: ClosedReceiveChannelException) { + // Registration queue closed; stop registrator } - } catch (exception: ClosedReceiveChannelException) { - // Registration queue closed; stop registrator } - } private fun registerListener(listener: Messenger) { synchronized(this) { @@ -133,29 +125,28 @@ class ServiceEndpoint( listeners.put(listenerId, listener) - val initialEvents = mutableListOf( - Event.TunnelStateChange(connectionProxy.state), - Event.AccountHistoryEvent(accountCache.onAccountHistoryChange.latestEvent), - Event.SettingsUpdate(settingsListener.settings), - Event.NewLocation(locationInfoCache.location), - Event.SplitTunnelingUpdate(splitTunneling.onChange.latestEvent), - Event.CurrentVersion(appVersionInfoCache.currentVersion), - Event.AppVersionInfo(appVersionInfoCache.appVersionInfo), - Event.NewRelayList(relayListListener.relayList), - Event.AuthToken(authTokenCache.authToken), - Event.ListenerReady(messenger, listenerId) - ) + val initialEvents = + mutableListOf( + Event.TunnelStateChange(connectionProxy.state), + Event.AccountHistoryEvent(accountCache.onAccountHistoryChange.latestEvent), + Event.SettingsUpdate(settingsListener.settings), + Event.NewLocation(locationInfoCache.location), + Event.SplitTunnelingUpdate(splitTunneling.onChange.latestEvent), + Event.CurrentVersion(appVersionInfoCache.currentVersion), + Event.AppVersionInfo(appVersionInfoCache.appVersionInfo), + Event.NewRelayList(relayListListener.relayList), + Event.AuthToken(authTokenCache.authToken), + Event.ListenerReady(messenger, listenerId) + ) if (vpnPermission.waitingForResponse) { initialEvents.add(Event.VpnPermissionRequest) } - val didSuccessfullySendAllMessages = initialEvents.all { event -> - listener.trySendEvent( - event, - SHOULD_LOG_DEAD_OBJECT_EXCEPTION - ) - } + val didSuccessfullySendAllMessages = + initialEvents.all { event -> + listener.trySendEvent(event, SHOULD_LOG_DEAD_OBJECT_EXCEPTION) + } if (didSuccessfullySendAllMessages.not()) { listeners.remove(listenerId) } @@ -163,9 +154,7 @@ class ServiceEndpoint( } private fun unregisterListener(listenerId: Int) { - synchronized(this) { - listeners.remove(listenerId) - } + synchronized(this) { listeners.remove(listenerId) } } private fun newListenerId(): Int { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt index b44daaf0d6..772e3127b9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SettingsListener.kt @@ -69,9 +69,7 @@ class SettingsListener(endpoint: ServiceEndpoint) { fun subscribe(id: Any, listener: (Settings) -> Unit) { settingsNotifier.subscribe(id) { maybeSettings -> - maybeSettings?.let { settings -> - listener(settings) - } + maybeSettings?.let { settings -> listener(settings) } } } @@ -84,9 +82,7 @@ class SettingsListener(endpoint: ServiceEndpoint) { } private fun fetchInitialSettings(daemon: MullvadDaemon) { - synchronized(this) { - handleNewSettings(daemon.getSettings()) - } + synchronized(this) { handleNewSettings(daemon.getSettings()) } } private fun handleNewSettings(newSettings: Settings?) { @@ -105,17 +101,19 @@ class SettingsListener(endpoint: ServiceEndpoint) { } } - private fun spawnActor() = GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { - try { - for (command in channel) { - when (command) { - is Command.SetAllowLan -> daemon.await().setAllowLan(command.allow) - is Command.SetAutoConnect -> daemon.await().setAutoConnect(command.autoConnect) - is Command.SetWireGuardMtu -> daemon.await().setWireguardMtu(command.mtu) + private fun spawnActor() = + GlobalScope.actor<Command>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (command in channel) { + when (command) { + is Command.SetAllowLan -> daemon.await().setAllowLan(command.allow) + is Command.SetAutoConnect -> + daemon.await().setAutoConnect(command.autoConnect) + is Command.SetWireGuardMtu -> daemon.await().setWireguardMtu(command.mtu) + } } + } catch (exception: ClosedReceiveChannelException) { + // Closed sender, so stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Closed sender, so stop the actor } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SplitTunneling.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SplitTunneling.kt index d6455ea9a3..6ae5ce8ef4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SplitTunneling.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/SplitTunneling.kt @@ -9,12 +9,13 @@ import net.mullvad.talpid.util.EventNotifier class SplitTunneling(persistence: SplitTunnelingPersistence, endpoint: ServiceEndpoint) { private val excludedApps = persistence.excludedApps.toMutableSet() - private var enabled by observable(persistence.enabled) { _, wasEnabled, isEnabled -> - if (wasEnabled != isEnabled) { - persistence.enabled = isEnabled - update() + private var enabled by + observable(persistence.enabled) { _, wasEnabled, isEnabled -> + if (wasEnabled != isEnabled) { + persistence.enabled = isEnabled + update() + } } - } val onChange = EventNotifier<List<String>?>(excludedApps.toList()) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VoucherRedeemer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VoucherRedeemer.kt index c1433b7a3c..850afa6d90 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VoucherRedeemer.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VoucherRedeemer.kt @@ -25,15 +25,16 @@ class VoucherRedeemer(private val endpoint: ServiceEndpoint) { voucherChannel.close() } - private fun spawnActor() = GlobalScope.actor<String>(Dispatchers.Default, Channel.UNLIMITED) { - try { - for (voucher in channel) { - val result = daemon.await().submitVoucher(voucher) + private fun spawnActor() = + GlobalScope.actor<String>(Dispatchers.Default, Channel.UNLIMITED) { + try { + for (voucher in channel) { + val result = daemon.await().submitVoucher(voucher) - endpoint.sendEvent(Event.VoucherSubmissionResult(voucher, result)) + endpoint.sendEvent(Event.VoucherSubmissionResult(voucher, result)) + } + } catch (exception: ClosedReceiveChannelException) { + // Voucher channel was closed, stop the actor } - } catch (exception: ClosedReceiveChannelException) { - // Voucher channel was closed, stop the actor } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt index b92dab3f98..b5b4875e04 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/endpoint/VpnPermission.kt @@ -27,10 +27,11 @@ class VpnPermission(private val context: Context, private val endpoint: ServiceE if (intent == null) { isGranted.update(true) } else { - val activityIntent = Intent(context, MainActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - } + val activityIntent = + Intent(context, MainActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + } isGranted.update(null) waitingForResponse = true diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt index cdad8e327f..ac3fbcb139 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/AccountExpiryNotification.kt @@ -36,29 +36,27 @@ class AccountExpiryNotification( private val buyMoreTimeUrl = resources.getString(R.string.account_url) - private val channel = NotificationChannel( - context, - "mullvad_account_time", - NotificationCompat.VISIBILITY_PRIVATE, - R.string.account_time_notification_channel_name, - R.string.account_time_notification_channel_description, - NotificationManager.IMPORTANCE_HIGH, - true, - true - ) + private val channel = + NotificationChannel( + context, + "mullvad_account_time", + NotificationCompat.VISIBILITY_PRIVATE, + R.string.account_time_notification_channel_name, + R.string.account_time_notification_channel_description, + NotificationManager.IMPORTANCE_HIGH, + true, + true + ) - var accountExpiry by observable<AccountExpiry>( - AccountExpiry.Missing - ) { _, oldValue, newValue -> - if (oldValue != newValue) { - jobTracker.newUiJob("update") { update(newValue) } + var accountExpiry by + observable<AccountExpiry>(AccountExpiry.Missing) { _, oldValue, newValue -> + if (oldValue != newValue) { + jobTracker.newUiJob("update") { update(newValue) } + } } - } init { - accountCache.onAccountExpiryChange.subscribe(this) { expiry -> - accountExpiry = expiry - } + accountCache.onAccountExpiryChange.subscribe(this) { expiry -> accountExpiry = expiry } } fun onDestroy() { @@ -95,9 +93,10 @@ class AccountExpiryNotification( } private suspend fun build(expiry: DateTime, remainingTime: Duration): Notification { - val url = jobTracker.runOnBackground { - Uri.parse("$buyMoreTimeUrl?token=${daemon.await().getWwwAuthToken()}") - } + val url = + jobTracker.runOnBackground { + Uri.parse("$buyMoreTimeUrl?token=${daemon.await().getWwwAuthToken()}") + } val intent = Intent(Intent.ACTION_VIEW, url) val pendingIntent = PendingIntent.getActivity(context, 1, intent, SdkUtils.getSupportedPendingIntentFlags()) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt index 052866b549..de557aaf22 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/NotificationChannel.kt @@ -18,9 +18,7 @@ class NotificationChannel( isVibrationEnabled: Boolean, isBadgeEnabled: Boolean ) { - private val badgeColor by lazy { - context.getColor(R.color.colorPrimary) - } + private val badgeColor by lazy { context.getColor(R.color.colorPrimary) } val notificationManager = NotificationManagerCompat.from(context) @@ -28,12 +26,13 @@ class NotificationChannel( val channelName = context.getString(name) val channelDescription = context.getString(description) - val channel = NotificationChannelCompat.Builder(id, importance) - .setName(channelName) - .setDescription(channelDescription) - .setShowBadge(isBadgeEnabled) - .setVibrationEnabled(isVibrationEnabled) - .build() + val channel = + NotificationChannelCompat.Builder(id, importance) + .setName(channelName) + .setDescription(channelDescription) + .setShowBadge(isBadgeEnabled) + .setVibrationEnabled(isVibrationEnabled) + .build() notificationManager.createNotificationChannel(channel) } @@ -79,20 +78,19 @@ class NotificationChannel( deleteIntent: PendingIntent? = null, isOngoing: Boolean = false ): Notification { - val builder = NotificationCompat.Builder(context, id) - .setSmallIcon(R.drawable.small_logo_black) - .setColor(badgeColor) - .setContentTitle(title) - .setContentIntent(pendingIntent) - .setVisibility(visibility) - .setOngoing(isOngoing) + val builder = + NotificationCompat.Builder(context, id) + .setSmallIcon(R.drawable.small_logo_black) + .setColor(badgeColor) + .setContentTitle(title) + .setContentIntent(pendingIntent) + .setVisibility(visibility) + .setOngoing(isOngoing) for (action in actions) { builder.addAction(action) } - deleteIntent?.let { intent -> - builder.setDeleteIntent(intent) - } + deleteIntent?.let { intent -> builder.setDeleteIntent(intent) } return builder.build() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt index 06c77e5960..0a2aa186f8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotification.kt @@ -19,68 +19,74 @@ class TunnelStateNotification(val context: Context) { val NOTIFICATION_ID: Int = 1 } - private val channel = NotificationChannel( - context, - "vpn_tunnel_status", - NotificationCompat.VISIBILITY_SECRET, - R.string.foreground_notification_channel_name, - R.string.foreground_notification_channel_description, - NotificationManager.IMPORTANCE_MIN, - false, - false - ) + private val channel = + NotificationChannel( + context, + "vpn_tunnel_status", + NotificationCompat.VISIBILITY_SECRET, + R.string.foreground_notification_channel_name, + R.string.foreground_notification_channel_description, + NotificationManager.IMPORTANCE_MIN, + false, + false + ) private val notificationText: Int - get() = when (val state = tunnelState) { - is TunnelState.Disconnected -> R.string.unsecured - is TunnelState.Connecting -> { - if (reconnecting) { - R.string.reconnecting - } else { - R.string.connecting + get() = + when (val state = tunnelState) { + is TunnelState.Disconnected -> R.string.unsecured + is TunnelState.Connecting -> { + if (reconnecting) { + R.string.reconnecting + } else { + R.string.connecting + } } - } - is TunnelState.Connected -> R.string.secured - is TunnelState.Disconnecting -> { - when (state.actionAfterDisconnect) { - ActionAfterDisconnect.Reconnect -> R.string.reconnecting - else -> R.string.disconnecting + is TunnelState.Connected -> R.string.secured + is TunnelState.Disconnecting -> { + when (state.actionAfterDisconnect) { + ActionAfterDisconnect.Reconnect -> R.string.reconnecting + else -> R.string.disconnecting + } + } + is TunnelState.Error -> { + state.errorState.getErrorNotificationResources(context).titleResourceId } } - is TunnelState.Error -> { - state.errorState.getErrorNotificationResources(context).titleResourceId - } - } private val shouldDisplayOngoingNotification: Boolean - get() = when (tunnelState) { - is TunnelState.Connected -> true - is TunnelState.Disconnected, - is TunnelState.Connecting, - is TunnelState.Disconnecting, - is TunnelState.Error -> false - } + get() = + when (tunnelState) { + is TunnelState.Connected -> true + is TunnelState.Disconnected, + is TunnelState.Connecting, + is TunnelState.Disconnecting, + is TunnelState.Error -> false + } private var reconnecting = false private var showingReconnecting = false var showAction by observable(false) { _, _, _ -> update() } - var tunnelState by observable<TunnelState>(TunnelState.Disconnected) { _, _, newState -> - val isReconnecting = newState is TunnelState.Connecting && reconnecting - val shouldBeginReconnecting = (newState as? TunnelState.Disconnecting) - ?.actionAfterDisconnect == ActionAfterDisconnect.Reconnect - reconnecting = isReconnecting || shouldBeginReconnecting - update() - } - - var visible by observable(true) { _, _, newValue -> - if (newValue == true) { + var tunnelState by + observable<TunnelState>(TunnelState.Disconnected) { _, _, newState -> + val isReconnecting = newState is TunnelState.Connecting && reconnecting + val shouldBeginReconnecting = + (newState as? TunnelState.Disconnecting)?.actionAfterDisconnect == + ActionAfterDisconnect.Reconnect + reconnecting = isReconnecting || shouldBeginReconnecting update() - } else { - channel.notificationManager.cancel(NOTIFICATION_ID) } - } + + var visible by + observable(true) { _, _, newValue -> + if (newValue == true) { + update() + } else { + channel.notificationManager.cancel(NOTIFICATION_ID) + } + } private fun update() { if (visible && (!reconnecting || !showingReconnecting)) { @@ -89,20 +95,18 @@ class TunnelStateNotification(val context: Context) { } fun build(): Notification { - val intent = Intent(context, MainActivity::class.java) - .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) - .setAction(Intent.ACTION_MAIN) - val pendingIntent = PendingIntent.getActivity( - context, - 1, - intent, - SdkUtils.getSupportedPendingIntentFlags() - ) - val actions = if (showAction) { - listOf(buildAction()) - } else { - emptyList() - } + val intent = + Intent(context, MainActivity::class.java) + .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + .setAction(Intent.ACTION_MAIN) + val pendingIntent = + PendingIntent.getActivity(context, 1, intent, SdkUtils.getSupportedPendingIntentFlags()) + val actions = + if (showAction) { + listOf(buildAction()) + } else { + emptyList() + } return channel.buildNotification( pendingIntent, @@ -116,12 +120,13 @@ class TunnelStateNotification(val context: Context) { val action = TunnelStateNotificationAction.from(tunnelState) val label = context.getString(action.text) val intent = Intent(action.key).setPackage("net.mullvad.mullvadvpn") - val pendingIntent = PendingIntent.getForegroundService( - context, - 1, - intent, - SdkUtils.getSupportedPendingIntentFlags() - ) + val pendingIntent = + PendingIntent.getForegroundService( + context, + 1, + intent, + SdkUtils.getSupportedPendingIntentFlags() + ) return NotificationCompat.Action(action.icon, label, pendingIntent) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt index 714264efbf..9ed9998054 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/notifications/TunnelStateNotificationAction.kt @@ -12,43 +12,47 @@ enum class TunnelStateNotificationAction { Dismiss; companion object { - fun from(tunnelState: TunnelState) = when (tunnelState) { - is TunnelState.Disconnected -> Connect - is TunnelState.Connecting -> Cancel - is TunnelState.Connected -> Disconnect - is TunnelState.Disconnecting -> { - when (tunnelState.actionAfterDisconnect) { - ActionAfterDisconnect.Reconnect -> Cancel - else -> Connect + fun from(tunnelState: TunnelState) = + when (tunnelState) { + is TunnelState.Disconnected -> Connect + is TunnelState.Connecting -> Cancel + is TunnelState.Connected -> Disconnect + is TunnelState.Disconnecting -> { + when (tunnelState.actionAfterDisconnect) { + ActionAfterDisconnect.Reconnect -> Cancel + else -> Connect + } } - } - is TunnelState.Error -> { - if (tunnelState.errorState.isBlocking) { - Disconnect - } else { - Dismiss + is TunnelState.Error -> { + if (tunnelState.errorState.isBlocking) { + Disconnect + } else { + Dismiss + } } } - } } val text - get() = when (this) { - Connect -> R.string.connect - Disconnect -> R.string.disconnect - Cancel -> R.string.cancel - Dismiss -> R.string.dismiss - } + get() = + when (this) { + Connect -> R.string.connect + Disconnect -> R.string.disconnect + Cancel -> R.string.cancel + Dismiss -> R.string.dismiss + } val key - get() = when (this) { - Connect -> MullvadVpnService.KEY_CONNECT_ACTION - else -> MullvadVpnService.KEY_DISCONNECT_ACTION - } + get() = + when (this) { + Connect -> MullvadVpnService.KEY_CONNECT_ACTION + else -> MullvadVpnService.KEY_DISCONNECT_ACTION + } val icon - get() = when (this) { - Connect -> R.drawable.icon_notification_connect - else -> R.drawable.icon_notification_disconnect - } + get() = + when (this) { + Connect -> R.drawable.icon_notification_connect + else -> R.drawable.icon_notification_disconnect + } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/persistence/SplitTunnelingPersistence.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/persistence/SplitTunnelingPersistence.kt index 425aec8836..264304ab3f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/persistence/SplitTunnelingPersistence.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/service/persistence/SplitTunnelingPersistence.kt @@ -15,16 +15,18 @@ class SplitTunnelingPersistence(context: Context) { private val appListFile = File(context.filesDir, "split-tunnelling.txt") private val preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE) - var enabled by observable(preferences.getBoolean(KEY_ENABLED, false)) { _, _, isEnabled -> - preferences.edit().apply { - putBoolean(KEY_ENABLED, isEnabled) - apply() + var enabled by + observable(preferences.getBoolean(KEY_ENABLED, false)) { _, _, isEnabled -> + preferences.edit().apply { + putBoolean(KEY_ENABLED, isEnabled) + apply() + } } - } - var excludedApps by observable(loadExcludedApps()) { _, _, excludedAppsSet -> - appListFile.writeText(excludedAppsSet.joinToString(separator = "\n")) - } + var excludedApps by + observable(loadExcludedApps()) { _, _, excludedAppsSet -> + appListFile.writeText(excludedAppsSet.joinToString(separator = "\n")) + } private fun loadExcludedApps(): Set<String> { return when { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/BlockingController.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/BlockingController.kt index a98394001e..9589c9474b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/BlockingController.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/BlockingController.kt @@ -11,12 +11,13 @@ class BlockingController(val blockableView: BlockableView) { fun action() { if (!(job?.isActive ?: false)) { - job = GlobalScope.launch(Dispatchers.Main) { - blockableView.setEnabled(false) - innerJob = blockableView.onClick() - innerJob?.join() - blockableView.setEnabled(true) - } + job = + GlobalScope.launch(Dispatchers.Main) { + blockableView.setEnabled(false) + innerJob = blockableView.onClick() + innerJob?.join() + blockableView.setEnabled(true) + } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CollapsibleTitleController.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CollapsibleTitleController.kt index dcde70b923..658fd3fed3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CollapsibleTitleController.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CollapsibleTitleController.kt @@ -46,74 +46,80 @@ class CollapsibleTitleController(val parentView: View, scrollAreaId: Int = R.id. private val xOffsetInterpolation = LinearInterpolation() private val yOffsetInterpolation = LinearInterpolation() - private val collapsedTitleLayoutListener: LayoutListener = LayoutListener() { collapsedTitle -> - val (x, y) = calculateViewCoordinates(collapsedTitle) + private val collapsedTitleLayoutListener: LayoutListener = + LayoutListener() { collapsedTitle -> + val (x, y) = calculateViewCoordinates(collapsedTitle) - collapsedTitleHeight = collapsedTitle.height.toFloat() + collapsedTitleHeight = collapsedTitle.height.toFloat() - scaleInterpolation.end = collapsedTitleHeight / maxOf(1.0f, titleHeight) - xOffsetInterpolation.end = x - yOffsetInterpolation.end = y - } + scaleInterpolation.end = collapsedTitleHeight / maxOf(1.0f, titleHeight) + xOffsetInterpolation.end = x + yOffsetInterpolation.end = y + } - private val collapsedTitleView = parentView.findViewById<View>(R.id.collapsed_title).apply { - addOnLayoutChangeListener(collapsedTitleLayoutListener) - visibility = View.INVISIBLE - } + private val collapsedTitleView = + parentView.findViewById<View>(R.id.collapsed_title).apply { + addOnLayoutChangeListener(collapsedTitleLayoutListener) + visibility = View.INVISIBLE + } - private val expandedTitleLayoutListener: LayoutListener = LayoutListener() { expandedTitle -> - val (x, y) = calculateViewCoordinates(expandedTitle) + private val expandedTitleLayoutListener: LayoutListener = + LayoutListener() { expandedTitle -> + val (x, y) = calculateViewCoordinates(expandedTitle) - val expandedTitleMarginTop = when (val layoutParams = expandedTitle.layoutParams) { - is MarginLayoutParams -> layoutParams.topMargin - else -> 0 - } + val expandedTitleMarginTop = + when (val layoutParams = expandedTitle.layoutParams) { + is MarginLayoutParams -> layoutParams.topMargin + else -> 0 + } - expandedTitleHeight = expandedTitle.height.toFloat() + expandedTitleHeight = expandedTitle.height.toFloat() - scaleInterpolation.start = expandedTitleHeight / maxOf(1.0f, titleHeight) - xOffsetInterpolation.start = x - yOffsetInterpolation.start = y + scaleInterpolation.start = expandedTitleHeight / maxOf(1.0f, titleHeight) + xOffsetInterpolation.start = x + yOffsetInterpolation.start = y - scrollInterpolation.end = expandedTitleHeight + expandedTitleMarginTop - } + scrollInterpolation.end = expandedTitleHeight + expandedTitleMarginTop + } - private val titleLayoutListener: LayoutListener = LayoutListener() { title -> - val (x, y) = calculateViewCoordinates(title) + private val titleLayoutListener: LayoutListener = + LayoutListener() { title -> + val (x, y) = calculateViewCoordinates(title) - titleWidth = title.width.toFloat() - titleHeight = title.height.toFloat() + titleWidth = title.width.toFloat() + titleHeight = title.height.toFloat() - scaleInterpolation.start = expandedTitleHeight / maxOf(1.0f, titleHeight) - scaleInterpolation.end = collapsedTitleHeight / maxOf(1.0f, titleHeight) - xOffsetInterpolation.reference = x - yOffsetInterpolation.reference = y - } + scaleInterpolation.start = expandedTitleHeight / maxOf(1.0f, titleHeight) + scaleInterpolation.end = collapsedTitleHeight / maxOf(1.0f, titleHeight) + xOffsetInterpolation.reference = x + yOffsetInterpolation.reference = y + } - private val titleView = parentView.findViewById<View>(R.id.title).apply { - addOnLayoutChangeListener(titleLayoutListener) + private val titleView = + parentView.findViewById<View>(R.id.title).apply { + addOnLayoutChangeListener(titleLayoutListener) - // Setting the scale pivot point to the left corner simplifies the calculations - pivotX = 0.0f - pivotY = 0.0f - } + // Setting the scale pivot point to the left corner simplifies the calculations + pivotX = 0.0f + pivotY = 0.0f + } - private val scrollAreaLayoutListener: LayoutListener = LayoutListener() { - scrollOffset = scrollArea.verticalScrollOffset.toFloat() - } + private val scrollAreaLayoutListener: LayoutListener = + LayoutListener() { scrollOffset = scrollArea.verticalScrollOffset.toFloat() } - private val scrollArea = parentView.findViewById<View>(scrollAreaId).let { view -> - val scrollableView = view as ListenableScrollableView + private val scrollArea = + parentView.findViewById<View>(scrollAreaId).let { view -> + val scrollableView = view as ListenableScrollableView - view.addOnLayoutChangeListener(scrollAreaLayoutListener) + view.addOnLayoutChangeListener(scrollAreaLayoutListener) - scrollableView.onScrollListener = { _, top, _, _ -> - scrollOffset = top.toFloat() - update() - } + scrollableView.onScrollListener = { _, top, _, _ -> + scrollOffset = top.toFloat() + update() + } - scrollableView - } + scrollableView + } private var scrollOffsetUpdated = false get() { @@ -130,23 +136,25 @@ class CollapsibleTitleController(val parentView: View, scrollAreaId: Int = R.id. private var titleWidth = 0.0f private var titleHeight = 0.0f - private var scrollOffset: Float by observable(0.0f) { _, old, new -> - if (scrollOffsetUpdated == false && old != new) { - scrollOffsetUpdated = true + private var scrollOffset: Float by + observable(0.0f) { _, old, new -> + if (scrollOffsetUpdated == false && old != new) { + scrollOffsetUpdated = true + } } - } val fullCollapseScrollOffset: Float get() = scrollInterpolation.end - var expandedTitleView by observable<View?>(null) { _, oldView, newView -> - oldView?.removeOnLayoutChangeListener(expandedTitleLayoutListener) - newView?.apply { - addOnLayoutChangeListener(expandedTitleLayoutListener) - expandedTitleLayoutListener.listener(this) - visibility = View.INVISIBLE + var expandedTitleView by + observable<View?>(null) { _, oldView, newView -> + oldView?.removeOnLayoutChangeListener(expandedTitleLayoutListener) + newView?.apply { + addOnLayoutChangeListener(expandedTitleLayoutListener) + expandedTitleLayoutListener.listener(this) + visibility = View.INVISIBLE + } } - } init { expandedTitleView = parentView.findViewById<View>(R.id.expanded_title) @@ -170,11 +178,12 @@ class CollapsibleTitleController(val parentView: View, scrollAreaId: Int = R.id. yOffsetInterpolation.updated if (shouldUpdate) { - val progress = if (expandedTitleView != null) { - maxOf(0.0f, minOf(1.0f, scrollInterpolation.progress(scrollOffset))) - } else { - 1.0f - } + val progress = + if (expandedTitleView != null) { + maxOf(0.0f, minOf(1.0f, scrollInterpolation.progress(scrollOffset))) + } else { + 1.0f + } val scale = scaleInterpolation.interpolate(progress) val offsetX = xOffsetInterpolation.interpolate(progress) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectActionButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectActionButton.kt index 7fbc0875f5..664eb1bc49 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectActionButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ConnectActionButton.kt @@ -70,10 +70,11 @@ class ConnectActionButton(val parentView: View) { reconnectButton.addOnLayoutChangeListener { _, left, _, right, _, _, _, _, _ -> val width = right - left val layoutParams = reconnectButton.layoutParams - val leftMargin = when (layoutParams) { - is MarginLayoutParams -> layoutParams.leftMargin - else -> 0 - } + val leftMargin = + when (layoutParams) { + is MarginLayoutParams -> layoutParams.leftMargin + else -> 0 + } reconnectButtonSpace = width + leftMargin } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CustomTransformationMethod.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CustomTransformationMethod.kt index 9ec42e3b9f..083258c62d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CustomTransformationMethod.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/CustomTransformationMethod.kt @@ -29,7 +29,8 @@ class GroupedTransformationMethod() : TransformationMethod { class GroupedPasswordTransformationMethod() : PasswordTransformationMethod() { override fun getTransformation(source: CharSequence?, view: View?): CharSequence { return if (source != null && view != null) { - super.getTransformation(source, view)?.toString() + super.getTransformation(source, view) + ?.toString() ?.replace(DOT_CHAR, BIG_DOT_CHAR) ?.groupWithSpaces() ?: EMPTY_STRING diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemsAdapter.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemsAdapter.kt index a97f2af3d2..4676a3532a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemsAdapter.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/ListItemsAdapter.kt @@ -26,8 +26,10 @@ class ListItemsAdapter : RecyclerView.Adapter<ListItemsAdapter.ViewHolder>() { fun setItems(items: List<ListItemData?>) = listDiffer.submitList(items) - override fun onCreateViewHolder(parent: ViewGroup, @ListItemData.ItemType viewType: Int): - ListItemsAdapter.ViewHolder { + override fun onCreateViewHolder( + parent: ViewGroup, + @ListItemData.ItemType viewType: Int + ): ListItemsAdapter.ViewHolder { return ViewHolder( when (viewType) { ListItemData.DIVIDER -> DividerGroupListItemView(parent.context) @@ -36,8 +38,7 @@ class ListItemsAdapter : RecyclerView.Adapter<ListItemsAdapter.ViewHolder>() { ListItemData.ACTION -> ActionListItemView(parent.context) ListItemData.APPLICATION -> ApplicationListItemView(parent.context) ListItemData.DOUBLE_ACTION -> TwoActionListItemView(parent.context) - else -> - throw IllegalArgumentException("View type '$viewType' is not supported") + else -> throw IllegalArgumentException("View type '$viewType' is not supported") } ) } @@ -54,8 +55,7 @@ class ListItemsAdapter : RecyclerView.Adapter<ListItemsAdapter.ViewHolder>() { override fun getItemCount(): Int = listDiffer.currentList.size - @ListItemData.ItemType - override fun getItemViewType(position: Int): Int = getItem(position).type + @ListItemData.ItemType override fun getItemViewType(position: Int): Int = getItem(position).type override fun getItemId(position: Int): Long = getId(getItem(position).identifier) @@ -109,9 +109,9 @@ class ListItemsAdapter : RecyclerView.Adapter<ListItemsAdapter.ViewHolder>() { private val idCounter = AtomicLong(0) private val mapIds = hashMapOf<String, Long>() - internal fun getId(stringId: String): Long = mapIds.computeIfAbsent(stringId) { - idCounter.decrementAndGet() - } + internal fun getId(stringId: String): Long = + mapIds.computeIfAbsent(stringId) { idCounter.decrementAndGet() } } } + typealias DiffCallback = DiffUtil.ItemCallback<ListItemData> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LocationInfo.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LocationInfo.kt index f12985157c..abe52eb3cd 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LocationInfo.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/LocationInfo.kt @@ -110,10 +110,11 @@ class LocationInfo( if (endpoint != null) { val host = endpoint.address.address.hostAddress val port = endpoint.address.port - val protocol = when (endpoint.protocol) { - TransportProtocol.Tcp -> context.getString(R.string.tcp) - TransportProtocol.Udp -> context.getString(R.string.udp) - } + val protocol = + when (endpoint.protocol) { + TransportProtocol.Tcp -> context.getString(R.string.tcp) + TransportProtocol.Udp -> context.getString(R.string.udp) + } inAddress.text = context.getString(R.string.in_address) + " $host:$port $protocol" } else { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt index a0fa0e613e..daef7ece5d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt @@ -90,11 +90,12 @@ open class MainActivity : FragmentActivity() { changelogViewModel = get() } - requestedOrientation = if (deviceIsTv) { - ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - } else { - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } + requestedOrientation = + if (deviceIsTv) { + ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } else { + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } super.onCreate(savedInstanceState) @@ -235,9 +236,7 @@ open class MainActivity : FragmentActivity() { ChangelogDialog( changesList = state.changes, version = BuildConfig.VERSION_NAME, - onDismiss = { - changelogViewModel.dismissChangelogDialog() - } + onDismiss = { changelogViewModel.dismissChangelogDialog() } ) } } @@ -270,21 +269,22 @@ open class MainActivity : FragmentActivity() { val isNewAccount = accountToken == accountRepository.cachedCreatedAccount.value val isExpired = isNewAccount.not() && isExpired(LOGIN_AWAIT_EXPIRY_MILLIS) - val fragment = when { - isNewAccount -> WelcomeFragment() - isExpired -> { - if (shouldDelayLogin) { - delay(LOGIN_DELAY_MILLIS) + val fragment = + when { + isNewAccount -> WelcomeFragment() + isExpired -> { + if (shouldDelayLogin) { + delay(LOGIN_DELAY_MILLIS) + } + OutOfTimeFragment() } - OutOfTimeFragment() - } - else -> { - if (shouldDelayLogin) { - delay(LOGIN_DELAY_MILLIS) + else -> { + if (shouldDelayLogin) { + delay(LOGIN_DELAY_MILLIS) + } + ConnectFragment() } - ConnectFragment() } - } supportFragmentManager.beginTransaction().apply { replace(R.id.main_fragment, fragment) @@ -298,7 +298,8 @@ open class MainActivity : FragmentActivity() { .filter { it is AccountExpiry.Available } .map { it.date()?.isBeforeNow } .first() - } ?: false + } + ?: false } private fun openLoginView() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt index ef8deeeb0e..e31b1dbf19 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PreferencesFragment.kt @@ -34,8 +34,7 @@ class PreferencesFragment : BaseFragment() { private lateinit var autoConnectToggle: ToggleCell private lateinit var titleController: CollapsibleTitleController - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -53,23 +52,27 @@ class PreferencesFragment : BaseFragment() { requireMainActivity().onBackPressed() } - allowLanToggle = view.findViewById<ToggleCell>(R.id.allow_lan).apply { - listener = { state -> - serviceConnectionManager.settingsListener()?.allowLan = when (state) { - CellSwitch.State.ON -> true - else -> false + allowLanToggle = + view.findViewById<ToggleCell>(R.id.allow_lan).apply { + listener = { state -> + serviceConnectionManager.settingsListener()?.allowLan = + when (state) { + CellSwitch.State.ON -> true + else -> false + } } } - } - autoConnectToggle = view.findViewById<ToggleCell>(R.id.auto_connect).apply { - listener = { state -> - serviceConnectionManager.settingsListener()?.autoConnect = when (state) { - CellSwitch.State.ON -> true - else -> false + autoConnectToggle = + view.findViewById<ToggleCell>(R.id.auto_connect).apply { + listener = { state -> + serviceConnectionManager.settingsListener()?.autoConnect = + when (state) { + CellSwitch.State.ON -> true + else -> false + } } } - } titleController = CollapsibleTitleController(view) @@ -82,9 +85,7 @@ class PreferencesFragment : BaseFragment() { } private fun CoroutineScope.launchUiSubscriptionsOnResume() = launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - launchSettingsSubscription() - } + repeatOnLifecycle(Lifecycle.State.RESUMED) { launchSettingsSubscription() } } private fun CoroutineScope.launchSettingsSubscription() = launch { @@ -96,9 +97,7 @@ class PreferencesFragment : BaseFragment() { emptyFlow() } } - .flatMapLatest { - callbackFlowFromNotifier(it.settingsListener.settingsNotifier) - } + .flatMapLatest { callbackFlowFromNotifier(it.settingsListener.settingsNotifier) } .collect { settings -> if (settings != null) { updateUi(settings) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PrivacyDisclaimerFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PrivacyDisclaimerFragment.kt index 56d4937037..6347bd0e56 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PrivacyDisclaimerFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/PrivacyDisclaimerFragment.kt @@ -52,10 +52,8 @@ class PrivacyDisclaimerFragment : Fragment(), StatusBarPainter, NavigationBarPai } private fun openPrivacyPolicy() { - val privacyPolicyUrlIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse(getString(R.string.faqs_and_guides_url)) - ) + val privacyPolicyUrlIntent = + Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.faqs_and_guides_url))) context?.startActivity(privacyPolicyUrlIntent) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SelectLocationFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SelectLocationFragment.kt index e0ef43d41e..b3396cde72 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SelectLocationFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SelectLocationFragment.kt @@ -53,20 +53,20 @@ class SelectLocationFragment : BaseFragment(), StatusBarPainter, NavigationBarPa private var loadingSpinner = CompletableDeferred<View>() private var relayListState = RelayListState.Initializing - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onAttach(context: Context) { super.onAttach(context) - relayListAdapter = RelayListAdapter(context.resources).apply { - onSelect = { relayItem -> - serviceConnectionManager.relayListListener()?.selectedRelayLocation = - relayItem?.location - serviceConnectionManager.connectionProxy()?.connect() - close() + relayListAdapter = + RelayListAdapter(context.resources).apply { + onSelect = { relayItem -> + serviceConnectionManager.relayListListener()?.selectedRelayLocation = + relayItem?.location + serviceConnectionManager.connectionProxy()?.connect() + close() + } } - } } override fun onCreate(savedInstanceState: Bundle?) { @@ -88,12 +88,14 @@ class SelectLocationFragment : BaseFragment(), StatusBarPainter, NavigationBarPa view.findViewById<CustomRecyclerView>(R.id.relay_list).apply { layoutManager = LinearLayoutManager(requireMainActivity()) - adapter = AdapterWithHeader(relayListAdapter, R.layout.select_location_header).apply { - onHeaderAvailable = { headerView -> - initializeLoadingSpinner(headerView) - titleController.expandedTitleView = headerView.findViewById(R.id.expanded_title) + adapter = + AdapterWithHeader(relayListAdapter, R.layout.select_location_header).apply { + onHeaderAvailable = { headerView -> + initializeLoadingSpinner(headerView) + titleController.expandedTitleView = + headerView.findViewById(R.id.expanded_title) + } } - } addItemDecoration( ListItemDividerDecoration( @@ -137,14 +139,11 @@ class SelectLocationFragment : BaseFragment(), StatusBarPainter, NavigationBarPa .flatMapLatest { state -> if (state is ServiceConnectionState.ConnectedReady) { callbackFlow { - state.container.relayListListener.onRelayListChange = - { list, item -> - this.trySend(Pair(list, item)) - } - - awaitClose { - state.container.relayListListener.onRelayListChange = null + state.container.relayListListener.onRelayListChange = { list, item -> + this.trySend(Pair(list, item)) } + + awaitClose { state.container.relayListListener.onRelayListChange = null } } } else { emptyFlow() @@ -187,9 +186,7 @@ class SelectLocationFragment : BaseFragment(), StatusBarPainter, NavigationBarPa // Because this method is executed inside a layout pass, hiding the spinner needs to be // done in a new job so that it is executed after the layout pass finishes and can // therefore schedule a new layout - jobTracker.newUiJob("hideLoadingSpinner") { - spinner.visibility = View.GONE - } + jobTracker.newUiJob("hideLoadingSpinner") { spinner.visibility = View.GONE } } loadingSpinner.complete(spinner) @@ -201,14 +198,15 @@ class SelectLocationFragment : BaseFragment(), StatusBarPainter, NavigationBarPa selectedItem: RelayItem? ) { val animationFinished = CompletableDeferred<Unit>() - val animationListener = object : AnimationListener { - override fun onAnimationEnd(animation: Animation) { - animationFinished.complete(Unit) - } + val animationListener = + object : AnimationListener { + override fun onAnimationEnd(animation: Animation) { + animationFinished.complete(Unit) + } - override fun onAnimationStart(animation: Animation) {} - override fun onAnimationRepeat(animation: Animation) {} - } + override fun onAnimationStart(animation: Animation) {} + override fun onAnimationRepeat(animation: Animation) {} + } val fadeOut = AnimationUtils.loadAnimation(requireMainActivity(), R.anim.fade_out).apply { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt index 53d7a7414e..f00ff44648 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/SettingsFragment.kt @@ -47,8 +47,7 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter private lateinit var advancedMenu: View private lateinit var titleController: CollapsibleTitleController - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -62,21 +61,22 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter ): View { val view = inflater.inflate(R.layout.settings, container, false) - view.findViewById<ImageButton>(R.id.close).setOnClickListener { - activity?.onBackPressed() - } + view.findViewById<ImageButton>(R.id.close).setOnClickListener { activity?.onBackPressed() } - accountMenu = view.findViewById<AccountCell>(R.id.account).apply { - targetFragment = AccountFragment::class - } + accountMenu = + view.findViewById<AccountCell>(R.id.account).apply { + targetFragment = AccountFragment::class + } - preferencesMenu = view.findViewById<NavigateCell>(R.id.preferences).apply { - targetFragment = PreferencesFragment::class - } + preferencesMenu = + view.findViewById<NavigateCell>(R.id.preferences).apply { + targetFragment = PreferencesFragment::class + } - advancedMenu = view.findViewById<NavigateCell>(R.id.advanced).apply { - targetFragment = AdvancedFragment::class - } + advancedMenu = + view.findViewById<NavigateCell>(R.id.advanced).apply { + targetFragment = AdvancedFragment::class + } view.findViewById<NavigateCell>(R.id.report_a_problem).apply { targetFragment = ProblemReportFragment::class @@ -149,20 +149,14 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter private fun CoroutineScope.luanchConfigureMenuOnDeviceChanges() = launch { deviceRepository.deviceState - .debounce { - it.addDebounceForUnknownState(UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS) - } - .collect { device -> - updateLoggedInStatus(device is DeviceState.LoggedIn) - } + .debounce { it.addDebounceForUnknownState(UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS) } + .collect { device -> updateLoggedInStatus(device is DeviceState.LoggedIn) } } private fun CoroutineScope.launchUpdateExpiryTextOnExpiryChanges() = launch { accountRepository.accountExpiryState .map { state -> state.date() } - .collect { expiryDate -> - accountMenu.accountExpiry = expiryDate - } + .collect { expiryDate -> accountMenu.accountExpiry = expiryDate } } private fun CoroutineScope.launchVersionInfoSubscription() = launch { @@ -174,26 +168,23 @@ class SettingsFragment : BaseFragment(), StatusBarPainter, NavigationBarPainter emptyFlow() } } - .collect { versionInfo -> - updateVersionInfo(versionInfo) - } + .collect { versionInfo -> updateVersionInfo(versionInfo) } } private fun updateLoggedInStatus(loggedIn: Boolean) { - val visibility = if (loggedIn) { - View.VISIBLE - } else { - View.GONE - } + val visibility = + if (loggedIn) { + View.VISIBLE + } else { + View.GONE + } accountMenu.visibility = visibility preferencesMenu.visibility = visibility advancedMenu.visibility = visibility } - private fun updateVersionInfo( - versionInfo: VersionInfo - ) { + private fun updateVersionInfo(versionInfo: VersionInfo) { appVersionMenu.updateAvailable = versionInfo.isOutdated || !versionInfo.isSupported appVersionMenu.version = versionInfo.currentVersion ?: "" } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/UnderNotificationBannerBehavior.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/UnderNotificationBannerBehavior.kt index 2b28f21ff1..8abf20bb12 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/UnderNotificationBannerBehavior.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/UnderNotificationBannerBehavior.kt @@ -8,10 +8,8 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout.Behavior import net.mullvad.mullvadvpn.R -class UnderNotificationBannerBehavior( - context: Context, - attributes: AttributeSet -) : Behavior<ScrollView>(context, attributes) { +class UnderNotificationBannerBehavior(context: Context, attributes: AttributeSet) : + Behavior<ScrollView>(context, attributes) { override fun layoutDependsOn(parent: CoordinatorLayout, body: ScrollView, dependency: View) = dependency.id == R.id.notification_banner @@ -20,11 +18,12 @@ class UnderNotificationBannerBehavior( body: ScrollView, dependency: View ): Boolean { - val newPaddingTop = if (dependency.visibility == View.VISIBLE) { - dependency.height + dependency.translationY.toInt() - } else { - 0 - } + val newPaddingTop = + if (dependency.visibility == View.VISIBLE) { + dependency.height + dependency.translationY.toInt() + } else { + 0 + } body.getChildAt(0).apply { if (paddingTop != newPaddingTop) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt index 3362aef52f..ecaa995e4a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/extension/ContextExtensions.kt @@ -13,21 +13,20 @@ private const val ALWAYS_ON_VPN_APP = "always_on_vpn_app" fun Context.openAccountPageInBrowser(authToken: String) { startActivity( - Intent( - Intent.ACTION_VIEW, - Uri.parse(getString(R.string.account_url) + "?token=$authToken") - ) + Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.account_url) + "?token=$authToken")) ) } fun Context.getAlwaysOnVpnAppName(): String? { return resolveAlwaysOnVpnPackageName() ?.let { currentAlwaysOnVpn -> - packageManager.getInstalledPackagesList(0) - .singleOrNull { - it.packageName == currentAlwaysOnVpn && it.packageName != packageName - } - }?.applicationInfo?.loadLabel(packageManager)?.toString() + packageManager.getInstalledPackagesList(0).singleOrNull { + it.packageName == currentAlwaysOnVpn && it.packageName != packageName + } + } + ?.applicationInfo + ?.loadLabel(packageManager) + ?.toString() } fun Fragment.requireMainActivity(): MainActivity { @@ -44,10 +43,7 @@ fun Fragment.requireMainActivity(): MainActivity { // Always-on VPN being disabled or not being able to read the state, NULL will be returned. fun Context.resolveAlwaysOnVpnPackageName(): String? { return try { - Settings.Secure.getString( - contentResolver, - ALWAYS_ON_VPN_APP - ) + Settings.Secure.getString(contentResolver, ALWAYS_ON_VPN_APP) } catch (ex: SecurityException) { null } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt index a1050273d2..c7146e941c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/AccountFragment.kt @@ -78,13 +78,15 @@ class AccountFragment : BaseFragment() { redeemVoucherButton.setEnabled(!value) } - private var isAccountNumberShown by observable(false) { _, _, doShow -> - accountNumberView.informationState = if (doShow) { - InformationView.Masking.Show(GroupedTransformationMethod()) - } else { - InformationView.Masking.Hide(GroupedPasswordTransformationMethod()) + private var isAccountNumberShown by + observable(false) { _, _, doShow -> + accountNumberView.informationState = + if (doShow) { + InformationView.Masking.Show(GroupedTransformationMethod()) + } else { + InformationView.Masking.Hide(GroupedPasswordTransformationMethod()) + } } - } private lateinit var accountExpiryView: InformationView private lateinit var accountNumberView: CopyableInformationView @@ -93,8 +95,7 @@ class AccountFragment : BaseFragment() { private lateinit var redeemVoucherButton: RedeemVoucherButton private lateinit var titleController: CollapsibleTitleController - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onAttach(activity: Activity) { super.onAttach(activity) @@ -117,33 +118,35 @@ class AccountFragment : BaseFragment() { requireMainActivity().onBackPressed() } - sitePaymentButton = view.findViewById<SitePaymentButton>(R.id.site_payment).apply { - newAccount = false + sitePaymentButton = + view.findViewById<SitePaymentButton>(R.id.site_payment).apply { + newAccount = false - setOnClickAction("openAccountPageInBrowser", jobTracker) { - setEnabled(false) - serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> - context.openAccountPageInBrowser(token) + setOnClickAction("openAccountPageInBrowser", jobTracker) { + setEnabled(false) + serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> + context.openAccountPageInBrowser(token) + } + setEnabled(true) + checkForAddedTime() } - setEnabled(true) - checkForAddedTime() } - } - redeemVoucherButton = view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { - prepare(parentFragmentManager, jobTracker) - } + redeemVoucherButton = + view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { + prepare(parentFragmentManager, jobTracker) + } view.findViewById<Button>(R.id.logout).setOnClickAction("logout", jobTracker) { accountRepository.logout() } - accountNumberView = view.findViewById<CopyableInformationView>(R.id.account_number).apply { - informationState = InformationView.Masking.Hide(GroupedPasswordTransformationMethod()) - onToggleMaskingClicked = { - isAccountNumberShown = isAccountNumberShown.not() + accountNumberView = + view.findViewById<CopyableInformationView>(R.id.account_number).apply { + informationState = + InformationView.Masking.Hide(GroupedPasswordTransformationMethod()) + onToggleMaskingClicked = { isAccountNumberShown = isAccountNumberShown.not() } } - } accountExpiryView = view.findViewById(R.id.account_expiry) deviceNameView = view.findViewById(R.id.device_name) @@ -184,8 +187,7 @@ class AccountFragment : BaseFragment() { } .collect { state -> accountNumberView.information = state.token() - deviceNameView.information = - state.deviceName()?.capitalizeFirstCharOfEachWord() + deviceNameView.information = state.deviceName()?.capitalizeFirstCharOfEachWord() } } } @@ -206,33 +208,29 @@ class AccountFragment : BaseFragment() { serviceConnectionManager.connectionState .flatMapLatest { state -> if (state is ServiceConnectionState.ConnectedReady) { - callbackFlowFromNotifier( - state.container.connectionProxy.onUiStateChange - ) + callbackFlowFromNotifier(state.container.connectionProxy.onUiStateChange) } else { emptyFlow() } } .collect { uiState -> - hasConnectivity = uiState is TunnelState.Connected || - uiState is TunnelState.Disconnected || - (uiState is TunnelState.Error && !uiState.errorState.isBlocking) - isOffline = uiState is TunnelState.Error && - uiState.errorState.cause is ErrorStateCause.IsOffline + hasConnectivity = + uiState is TunnelState.Connected || + uiState is TunnelState.Disconnected || + (uiState is TunnelState.Error && !uiState.errorState.isBlocking) + isOffline = + uiState is TunnelState.Error && + uiState.errorState.cause is ErrorStateCause.IsOffline } } } private fun CoroutineScope.launchRefreshDeviceStateAfterAnimation() = launch { - transitionFinishedFlow.collect { - deviceRepository.refreshDeviceState() - } + transitionFinishedFlow.collect { deviceRepository.refreshDeviceState() } } private fun checkForAddedTime() { - currentAccountExpiry?.let { expiry -> - oldAccountExpiry = expiry - } + currentAccountExpiry?.let { expiry -> oldAccountExpiry = expiry } } private fun updateAccountExpiry(accountExpiry: DateTime?) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt index 506177bba1..905cc889a9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/BaseFragment.kt @@ -12,17 +12,18 @@ import net.mullvad.mullvadvpn.util.transitionFinished abstract class BaseFragment : Fragment { constructor() : super() - constructor (@LayoutRes contentLayoutId: Int) : super(contentLayoutId) + constructor(@LayoutRes contentLayoutId: Int) : super(contentLayoutId) protected var transitionFinishedFlow: Flow<Unit> = emptyFlow() private set override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { - val zAdjustment = if (animationsToAdjustZorder.contains(nextAnim)) { - 1f - } else { - 0f - } + val zAdjustment = + if (animationsToAdjustZorder.contains(nextAnim)) { + 1f + } else { + 0f + } ViewCompat.setTranslationZ(requireView(), zAdjustment) return if (nextAnim != 0 && enter) { AnimationUtils.loadAnimation(context, nextAnim)?.apply { @@ -34,11 +35,12 @@ abstract class BaseFragment : Fragment { } companion object { - private val animationsToAdjustZorder = listOf( - R.anim.fragment_enter_from_right, - R.anim.fragment_exit_to_right, - R.anim.fragment_enter_from_bottom, - R.anim.fragment_exit_to_bottom - ) + private val animationsToAdjustZorder = + listOf( + R.anim.fragment_enter_from_right, + R.anim.fragment_exit_to_right, + R.anim.fragment_enter_from_bottom, + R.anim.fragment_exit_to_bottom + ) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConfirmNoEmailDialogFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConfirmNoEmailDialogFragment.kt index f4bc1dade1..d31c5551b5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConfirmNoEmailDialogFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConfirmNoEmailDialogFragment.kt @@ -33,9 +33,7 @@ class ConfirmNoEmailDialogFragment : DialogFragment() { ): View { val view = inflater.inflate(R.layout.confirm_no_email, container, false) - view.findViewById<Button>(R.id.back_button).setOnClickListener { - activity?.onBackPressed() - } + view.findViewById<Button>(R.id.back_button).setOnClickListener { activity?.onBackPressed() } view.findViewById<Button>(R.id.send_button).setOnClickListener { confirmNoEmail?.complete(true) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt index f06f420158..68dd3a9403 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ConnectFragment.kt @@ -68,8 +68,7 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { private lateinit var status: ConnectionStatus private lateinit var locationInfo: LocationInfo - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -83,9 +82,10 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { ): View? { val view = inflater.inflate(R.layout.connect, container, false) - headerBar = view.findViewById<HeaderBar>(R.id.header_bar).apply { - tunnelState = TunnelState.Disconnected - } + headerBar = + view.findViewById<HeaderBar>(R.id.header_bar).apply { + tunnelState = TunnelState.Disconnected + } accountExpiryNotification.onClick = { serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> @@ -95,20 +95,20 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { } } - notificationBanner = view.findViewById<NotificationBanner>(R.id.notification_banner).apply { - notifications.apply { - // NOTE: The order of below notifications is significant. - register(tunnelStateNotification) - register(versionInfoNotification) - register(accountExpiryNotification) + notificationBanner = + view.findViewById<NotificationBanner>(R.id.notification_banner).apply { + notifications.apply { + // NOTE: The order of below notifications is significant. + register(tunnelStateNotification) + register(versionInfoNotification) + register(accountExpiryNotification) + } } - } status = ConnectionStatus(view, requireMainActivity()) - locationInfo = LocationInfo(view, requireContext()) { - connectViewModel.toggleTunnelInfoExpansion() - } + locationInfo = + LocationInfo(view, requireContext()) { connectViewModel.toggleTunnelInfoExpansion() } actionButton = ConnectActionButton(view) @@ -119,9 +119,10 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { onDisconnect = { serviceConnectionManager.connectionProxy()?.disconnect() } } - switchLocationButton = view.findViewById<SwitchLocationButton>(R.id.switch_location).apply { - onClick = { openSwitchLocationScreen() } - } + switchLocationButton = + view.findViewById<SwitchLocationButton>(R.id.switch_location).apply { + onClick = { openSwitchLocationScreen() } + } return view } @@ -136,15 +137,16 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { paintNavigationBar(ContextCompat.getColor(requireContext(), R.color.blue)) } - val shared = serviceConnectionManager.connectionState - .flatMapLatest { state -> - if (state is ServiceConnectionState.ConnectedReady) { - flowOf(state.container) - } else { - emptyFlow() + val shared = + serviceConnectionManager.connectionState + .flatMapLatest { state -> + if (state is ServiceConnectionState.ConnectedReady) { + flowOf(state.container) + } else { + emptyFlow() + } } - } - .shareIn(lifecycleScope, SharingStarted.WhileSubscribed()) + .shareIn(lifecycleScope, SharingStarted.WhileSubscribed()) private fun CoroutineScope.launchUiSubscriptionsOnResume() = launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { @@ -164,9 +166,7 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { } private fun LocationInfoCache.locationCallbackFlow() = callbackFlow { - onNewLocation = { - this.trySend(it) - } + onNewLocation = { this.trySend(it) } awaitClose { onNewLocation = null } } @@ -177,9 +177,7 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { } private fun RelayListListener.relayListCallbackFlow() = callbackFlow { - onRelayListChange = { _, item -> - this.trySend(item) - } + onRelayListChange = { _, item -> this.trySend(item) } awaitClose { onRelayListChange = null } } @@ -208,17 +206,15 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { } private fun CoroutineScope.launchAccountExpirySubscription() = launch { - accountRepository.accountExpiryState - .collect { - accountExpiryNotification.updateAccountExpiry(it.date()) - } + accountRepository.accountExpiryState.collect { + accountExpiryNotification.updateAccountExpiry(it.date()) + } } private fun CoroutineScope.launchTunnelInfoExpansionSubscription() = launch { - connectViewModel.isTunnelInfoExpanded - .collect { isExpanded -> - locationInfo.isTunnelInfoExpanded = isExpanded - } + connectViewModel.isTunnelInfoExpanded.collect { isExpanded -> + locationInfo.isTunnelInfoExpanded = isExpanded + } } private fun updateTunnelState(uiState: TunnelState, realState: TunnelState) { @@ -259,7 +255,8 @@ class ConnectFragment : BaseFragment(), NavigationBarPainter { private fun TunnelState.isTunnelErrorStateDueToExpiredAccount(): Boolean { return ((this as? TunnelState.Error)?.errorState?.cause as? ErrorStateCause.AuthFailed) - ?.isCausedByExpiredAccount() ?: false + ?.isCausedByExpiredAccount() + ?: false } companion object { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt index 7e51e7a8e1..a382381cd8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceListFragment.kt @@ -64,23 +64,20 @@ class DeviceListFragment : Fragment() { private fun CoroutineScope.launchUiSubscriptionsOnResume() = launch { deviceListViewModel.toastMessages .flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED) - .collect { - Toast.makeText(context, it, Toast.LENGTH_SHORT).show() - } + .collect { Toast.makeText(context, it, Toast.LENGTH_SHORT).show() } } private fun openLoginView(doTriggerAutoLogin: Boolean) { parentActivity()?.clearBackStack() - val loginFragment = LoginFragment().apply { - if (doTriggerAutoLogin && deviceListViewModel.accountToken != null) { - arguments = Bundle().apply { - putString( - ACCOUNT_TOKEN_ARGUMENT_KEY, - deviceListViewModel.accountToken - ) + val loginFragment = + LoginFragment().apply { + if (doTriggerAutoLogin && deviceListViewModel.accountToken != null) { + arguments = + Bundle().apply { + putString(ACCOUNT_TOKEN_ARGUMENT_KEY, deviceListViewModel.accountToken) + } } } - } parentFragmentManager.beginTransaction().apply { replace(R.id.main_fragment, loginFragment) commitAllowingStateLoss() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt index 68eb41e9ac..f63e7d4322 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/DeviceRevokedFragment.kt @@ -30,13 +30,14 @@ class DeviceRevokedFragment : Fragment() { AppTheme { val state = deviceRevokedViewModel.uiState.collectAsState().value - val topColor = colorResource( - if (state == DeviceRevokedUiState.SECURED) { - R.color.green - } else { - R.color.red - } - ) + val topColor = + colorResource( + if (state == DeviceRevokedUiState.SECURED) { + R.color.green + } else { + R.color.red + } + ) ScaffoldWithTopBar( topBarColor = topColor, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt index ed9b298ed0..3168824c92 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/LoginFragment.kt @@ -44,8 +44,7 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { private lateinit var input: AccountInput private lateinit var createAccountButton: Button - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -66,10 +65,11 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { loggedInStatus = view.findViewById(R.id.logged_in_status) loginFailStatus = view.findViewById(R.id.login_fail_status) - accountLogin = view.findViewById<AccountLogin>(R.id.account_login).apply { - onLogin = loginViewModel::login - onClearHistory = loginViewModel::clearAccountHistory - } + accountLogin = + view.findViewById<AccountLogin>(R.id.account_login).apply { + onLogin = loginViewModel::login + onClearHistory = loginViewModel::clearAccountHistory + } createAccountButton = view.findViewById(R.id.create_account) createAccountButton.setOnClickAction( @@ -80,9 +80,8 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { scrollArea = view.findViewById(R.id.scroll_area) - background = view.findViewById<View>(R.id.contents).apply { - setOnClickListener { requestFocus() } - } + background = + view.findViewById<View>(R.id.contents).apply { setOnClickListener { requestFocus() } } scrollToShow(accountLogin) @@ -102,9 +101,7 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { false } } - input.onTextChanged.subscribe(this) { - createAccountButton.isEnabled = it.isEmpty() - } + input.onTextChanged.subscribe(this) { createAccountButton.isEnabled = it.isEmpty() } } override fun onResume() { @@ -155,41 +152,32 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { is LoginViewModel.LoginUiState.Default -> { showDefault() } - is LoginViewModel.LoginUiState.Success -> { // MainActivity responsible for transition to connect/out-of-time view. showLoggedIn() } - is LoginViewModel.LoginUiState.AccountCreated -> { // MainActivity responsible for transition to welcome view. } - is LoginViewModel.LoginUiState.CreatingAccount -> { showCreatingAccount() } - is LoginViewModel.LoginUiState.Loading -> { showLoading() } - is LoginViewModel.LoginUiState.InvalidAccountError -> { loginFailure(resources.getString(R.string.login_fail_description)) } - is LoginViewModel.LoginUiState.TooManyDevicesError -> { showLoading(overrideSpinnerWithErrorIcon = true) openDeviceListFragment(uiState.accountToken) } - is LoginViewModel.LoginUiState.TooManyDevicesMissingListError -> { loginFailure(context?.getString(R.string.failed_to_fetch_devices)) } - is LoginViewModel.LoginUiState.UnableToCreateAccountError -> { loginFailure(resources.getString(R.string.failed_to_create_account)) } - is LoginViewModel.LoginUiState.OtherError -> { loginFailure(resources.getString(R.string.error_occurred)) } @@ -198,9 +186,10 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { private fun openDeviceListFragment(accountToken: String) { - val deviceFragment = DeviceListFragment().apply { - arguments = Bundle().apply { putString(ACCOUNT_TOKEN_ARGUMENT_KEY, accountToken) } - } + val deviceFragment = + DeviceListFragment().apply { + arguments = Bundle().apply { putString(ACCOUNT_TOKEN_ARGUMENT_KEY, accountToken) } + } parentFragmentManager.beginTransaction().apply { setCustomAnimations( @@ -228,17 +217,19 @@ class LoginFragment : BaseFragment(), NavigationBarPainter { title.setText(R.string.logging_in_title) subtitle.setText(R.string.logging_in_description) - loggingInStatus.visibility = if (overrideSpinnerWithErrorIcon == false) { - View.VISIBLE - } else { - View.GONE - } + loggingInStatus.visibility = + if (overrideSpinnerWithErrorIcon == false) { + View.VISIBLE + } else { + View.GONE + } - loginFailStatus.visibility = if (overrideSpinnerWithErrorIcon == false) { - View.GONE - } else { - View.VISIBLE - } + loginFailStatus.visibility = + if (overrideSpinnerWithErrorIcon == false) { + View.GONE + } else { + View.VISIBLE + } loggedInStatus.visibility = View.GONE diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt index c1b4633692..8acb8e38df 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/OutOfTimeFragment.kt @@ -46,14 +46,14 @@ class OutOfTimeFragment : BaseFragment() { private lateinit var disconnectButton: Button private lateinit var redeemButton: RedeemVoucherButton - private var tunnelState by observable<TunnelState>(TunnelState.Disconnected) { _, _, state -> - updateDisconnectButton() - updateBuyButtons() - headerBar.tunnelState = state - } + private var tunnelState by + observable<TunnelState>(TunnelState.Disconnected) { _, _, state -> + updateDisconnectButton() + updateBuyButtons() + headerBar.tunnelState = state + } - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -67,9 +67,10 @@ class OutOfTimeFragment : BaseFragment() { ): View? { val view = inflater.inflate(R.layout.out_of_time, container, false) - headerBar = view.findViewById<HeaderBar>(R.id.header_bar).apply { - tunnelState = this@OutOfTimeFragment.tunnelState - } + headerBar = + view.findViewById<HeaderBar>(R.id.header_bar).apply { + tunnelState = this@OutOfTimeFragment.tunnelState + } view.findViewById<TextView>(R.id.account_credit_has_expired).text = buildString { append(requireActivity().getString(R.string.account_credit_has_expired)) @@ -77,29 +78,32 @@ class OutOfTimeFragment : BaseFragment() { requireActivity().getString(R.string.add_time_to_account) } - disconnectButton = view.findViewById<Button>(R.id.disconnect).apply { - setOnClickAction("disconnect", jobTracker) { - serviceConnectionManager.connectionProxy()?.disconnect() + disconnectButton = + view.findViewById<Button>(R.id.disconnect).apply { + setOnClickAction("disconnect", jobTracker) { + serviceConnectionManager.connectionProxy()?.disconnect() + } } - } - sitePaymentButton = view.findViewById<SitePaymentButton>(R.id.site_payment).apply { - newAccount = false + sitePaymentButton = + view.findViewById<SitePaymentButton>(R.id.site_payment).apply { + newAccount = false - setOnClickAction("openAccountPageInBrowser", jobTracker) { - setEnabled(false) - serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> - context.openAccountPageInBrowser(token) + setOnClickAction("openAccountPageInBrowser", jobTracker) { + setEnabled(false) + serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> + context.openAccountPageInBrowser(token) + } + setEnabled(true) } - setEnabled(true) - } - isEnabled = true - } + isEnabled = true + } - redeemButton = view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { - prepare(parentFragmentManager, jobTracker) - } + redeemButton = + view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { + prepare(parentFragmentManager, jobTracker) + } return view } @@ -120,9 +124,7 @@ class OutOfTimeFragment : BaseFragment() { private fun CoroutineScope.launchProceedToConnectViewIfExpiryExtended() = launch { accountRepository.accountExpiryState .map { state -> state.date() } - .collect { expiryDate -> - checkExpiry(expiryDate) - } + .collect { expiryDate -> checkExpiry(expiryDate) } } private fun CoroutineScope.launchExpiryPolling() = launch { @@ -136,29 +138,27 @@ class OutOfTimeFragment : BaseFragment() { serviceConnectionManager.connectionState .flatMapLatest { state -> if (state is ServiceConnectionState.ConnectedReady) { - callbackFlowFromNotifier( - state.container.connectionProxy.onStateChange - ) + callbackFlowFromNotifier(state.container.connectionProxy.onStateChange) } else { emptyFlow() } } - .collect { newState -> - tunnelState = newState - } + .collect { newState -> tunnelState = newState } } private fun updateDisconnectButton() { val state = tunnelState - val showButton = when (state) { - is TunnelState.Disconnected -> false - is TunnelState.Connecting, is TunnelState.Connected -> true - is TunnelState.Disconnecting -> { - state.actionAfterDisconnect != ActionAfterDisconnect.Nothing + val showButton = + when (state) { + is TunnelState.Disconnected -> false + is TunnelState.Connecting, + is TunnelState.Connected -> true + is TunnelState.Disconnecting -> { + state.actionAfterDisconnect != ActionAfterDisconnect.Nothing + } + is TunnelState.Error -> state.errorState.isBlocking } - is TunnelState.Error -> state.errorState.isBlocking - } disconnectButton.apply { if (showButton) { @@ -176,17 +176,16 @@ class OutOfTimeFragment : BaseFragment() { val hasConnectivity = currentState is TunnelState.Disconnected sitePaymentButton.setEnabled(hasConnectivity) - val isOffline = currentState is TunnelState.Error && - currentState.errorState.cause is ErrorStateCause.IsOffline + val isOffline = + currentState is TunnelState.Error && + currentState.errorState.cause is ErrorStateCause.IsOffline redeemButton.setEnabled(!isOffline) } private fun checkExpiry(maybeExpiry: DateTime?) { maybeExpiry?.let { expiry -> if (expiry.isAfterNow()) { - jobTracker.newUiJob("advanceToConnectScreen") { - advanceToConnectScreen() - } + jobTracker.newUiJob("advanceToConnectScreen") { advanceToConnectScreen() } } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt index 0e3a52d54b..d38b905a21 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ProblemReportFragment.kt @@ -28,15 +28,16 @@ import net.mullvad.mullvadvpn.util.JobTracker class ProblemReportFragment : BaseFragment() { private val jobTracker = JobTracker() - private var showingEmail by observable(false) { _, oldValue, newValue -> - if (oldValue != newValue) { - if (newValue == true) { - parentActivity.enterSecureScreen(this) - } else { - parentActivity.leaveSecureScreen(this) + private var showingEmail by + observable(false) { _, oldValue, newValue -> + if (oldValue != newValue) { + if (newValue == true) { + parentActivity.enterSecureScreen(this) + } else { + parentActivity.leaveSecureScreen(this) + } } } - } private lateinit var parentActivity: MainActivity private lateinit var problemReport: MullvadProblemReport @@ -76,9 +77,7 @@ class ProblemReportFragment : BaseFragment() { ): View { val view = inflater.inflate(R.layout.problem_report, container, false) - view.findViewById<View>(R.id.back).setOnClickListener { - activity?.onBackPressed() - } + view.findViewById<View>(R.id.back).setOnClickListener { activity?.onBackPressed() } bodyContainer = view.findViewById<ViewSwitcher>(R.id.body_container) userEmailInput = view.findViewById<EditText>(R.id.user_email) @@ -96,24 +95,14 @@ class ProblemReportFragment : BaseFragment() { editMessageButton = view.findViewById<Button>(R.id.edit_message_button) tryAgainButton = view.findViewById<Button>(R.id.try_again_button) - view.findViewById<Button>(R.id.view_logs).setOnClickListener { - showLogs() - } + view.findViewById<Button>(R.id.view_logs).setOnClickListener { showLogs() } - sendButton.setOnClickListener { - jobTracker.newUiJob("sendReport") { - sendReport(true) - } - } + sendButton.setOnClickListener { jobTracker.newUiJob("sendReport") { sendReport(true) } } - editMessageButton.setOnClickListener { - showForm() - } + editMessageButton.setOnClickListener { showForm() } tryAgainButton.setOnClickListener { - jobTracker.newUiJob("sendReport") { - sendReport(false) - } + jobTracker.newUiJob("sendReport") { sendReport(false) } } userEmailInput.setText(problemReport.userEmail) @@ -244,9 +233,10 @@ class ProblemReportFragment : BaseFragment() { val colorStyle = ForegroundColorSpan(parentActivity.getColor(R.color.green)) - sendDetailsLabel.text = SpannableStringBuilder("$thanks $weWillLookIntoThis").apply { - setSpan(colorStyle, 0, thanks.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - } + sendDetailsLabel.text = + SpannableStringBuilder("$thanks $weWillLookIntoThis").apply { + setSpan(colorStyle, 0, thanks.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } sendDetailsLabel.visibility = View.VISIBLE } @@ -262,10 +252,11 @@ class ProblemReportFragment : BaseFragment() { val boldStyle = StyleSpan(Typeface.BOLD) val colorStyle = ForegroundColorSpan(parentActivity.getColor(R.color.white)) - responseMessageLabel.text = SpannableStringBuilder(responseMessage).apply { - setSpan(boldStyle, emailStart, emailEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - setSpan(colorStyle, emailStart, emailEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - } + responseMessageLabel.text = + SpannableStringBuilder(responseMessage).apply { + setSpan(boldStyle, emailStart, emailEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + setSpan(colorStyle, emailStart, emailEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + } responseMessageLabel.visibility = View.VISIBLE diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt index 6efe55c118..a2c369bb15 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/RedeemVoucherDialogFragment.kt @@ -73,9 +73,10 @@ class RedeemVoucherDialogFragment : DialogFragment() { ): View { val view = inflater.inflate(R.layout.redeem_voucher, container, false) - voucherInput = view.findViewById<EditText>(R.id.voucher_code).apply { - addTextChangedListener(ValidVoucherCodeChecker()) - } + voucherInput = + view.findViewById<EditText>(R.id.voucher_code).apply { + addTextChangedListener(ValidVoucherCodeChecker()) + } SegmentedInputFormatter(voucherInput, '-').apply { allCaps = true @@ -85,13 +86,12 @@ class RedeemVoucherDialogFragment : DialogFragment() { } } - redeemButton = view.findViewById<Button>(R.id.redeem).apply { - setEnabled(false) + redeemButton = + view.findViewById<Button>(R.id.redeem).apply { + setEnabled(false) - setOnClickAction("action", jobTracker) { - submitVoucher() + setOnClickAction("action", jobTracker) { submitVoucher() } } - } errorMessage = view.findViewById(R.id.error) @@ -141,7 +141,9 @@ class RedeemVoucherDialogFragment : DialogFragment() { when (result) { is VoucherSubmissionResult.Ok -> handleAddedTime(result.submission.timeAdded) is VoucherSubmissionResult.Error -> showError(result.error) - else -> { /* NOOP */ } + else -> { + /* NOOP */ + } } } @@ -152,11 +154,12 @@ class RedeemVoucherDialogFragment : DialogFragment() { } private fun showError(error: VoucherSubmissionError) { - val message = when (error) { - VoucherSubmissionError.InvalidVoucher -> R.string.invalid_voucher - VoucherSubmissionError.VoucherAlreadyUsed -> R.string.voucher_already_used - else -> R.string.error_occurred - } + val message = + when (error) { + VoucherSubmissionError.InvalidVoucher -> R.string.invalid_voucher + VoucherSubmissionError.VoucherAlreadyUsed -> R.string.voucher_already_used + else -> R.string.error_occurred + } errorMessage.apply { setText(message) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt index e154037ca1..27db561ee9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SplitTunnelingFragment.kt @@ -37,28 +37,28 @@ import org.koin.core.scope.Scope class SplitTunnelingFragment : BaseFragment(R.layout.collapsed_title_layout) { private val listItemsAdapter = ListItemsAdapter() - private val scope: Scope = getKoin().getOrCreateScope(APPS_SCOPE, named(APPS_SCOPE)) - .also { appsScope -> + private val scope: Scope = + getKoin().getOrCreateScope(APPS_SCOPE, named(APPS_SCOPE)).also { appsScope -> getKoin().getScopeOrNull(SERVICE_CONNECTION_SCOPE)?.let { serviceConnectionScope -> appsScope.linkTo(serviceConnectionScope) } } - private val viewModel by scope.viewModel<SplitTunnelingViewModel>( - owner = { - ViewModelOwner.from(this, this) - } - ) + private val viewModel by + scope.viewModel<SplitTunnelingViewModel>(owner = { ViewModelOwner.from(this, this) }) private val toggleSystemAppsVisibility = Channel<Boolean>(Channel.CONFLATED) private val toggleExcludeChannel = Channel<ListItemData>(Channel.BUFFERED) - private val listItemListener = object : ListItemListener { - override fun onItemAction(item: ListItemData) { - when (item.widget) { - is ImageState -> toggleExcludeChannel.trySend(item) - is SwitchState -> toggleSystemAppsVisibility.trySend(!item.widget.isChecked) - else -> { /* NOOP */ } + private val listItemListener = + object : ListItemListener { + override fun onItemAction(item: ListItemData) { + when (item.widget) { + is ImageState -> toggleExcludeChannel.trySend(item) + is SwitchState -> toggleSystemAppsVisibility.trySend(!item.widget.isChecked) + else -> { + /* NOOP */ + } + } } } - } private var recyclerView: RecyclerView? = null @@ -69,32 +69,24 @@ class SplitTunnelingFragment : BaseFragment(R.layout.collapsed_title_layout) { } listItemsAdapter.listItemListener = listItemListener listItemsAdapter.setHasStableIds(true) - recyclerView = view.findViewById<RecyclerView>(R.id.recyclerView).apply { - adapter = listItemsAdapter - addItemDecoration( - ListItemDividerDecoration( - topOffset = resources.getDimensionPixelSize(R.dimen.list_item_divider) + recyclerView = + view.findViewById<RecyclerView>(R.id.recyclerView).apply { + adapter = listItemsAdapter + addItemDecoration( + ListItemDividerDecoration( + topOffset = resources.getDimensionPixelSize(R.dimen.list_item_divider) + ) ) - ) - tweakMargin(this) - } - view.findViewById<View>(R.id.back).setOnClickListener { - requireActivity().onBackPressed() - } + tweakMargin(this) + } + view.findViewById<View>(R.id.back).setOnClickListener { requireActivity().onBackPressed() } lifecycleScope.launchWhenStarted { - viewModel.listItems - .onEach { - listItemsAdapter.setItems(it) - } - .catch { } - .collect() + viewModel.listItems.onEach { listItemsAdapter.setItems(it) }.catch {}.collect() } lifecycleScope.launchWhenResumed { // pass view intent to view model - intents() - .onEach { viewModel.processIntent(it) } - .collect() + intents().onEach { viewModel.processIntent(it) }.collect() } } @@ -105,11 +97,12 @@ class SplitTunnelingFragment : BaseFragment(R.layout.collapsed_title_layout) { super.onDestroy() } - private fun intents(): Flow<ViewIntent> = merge( - transitionFinishedFlow.map { ViewIntent.ViewIsReady }, - toggleExcludeChannel.consumeAsFlow().map { ViewIntent.ChangeApplicationGroup(it) }, - toggleSystemAppsVisibility.consumeAsFlow().map { ViewIntent.ShowSystemApps(it) } - ) + private fun intents(): Flow<ViewIntent> = + merge( + transitionFinishedFlow.map { ViewIntent.ViewIsReady }, + toggleExcludeChannel.consumeAsFlow().map { ViewIntent.ChangeApplicationGroup(it) }, + toggleSystemAppsVisibility.consumeAsFlow().map { ViewIntent.ShowSystemApps(it) } + ) private fun tweakMargin(view: View) { if (!hasNavigationBar()) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt index 65f44770fb..b655c007c6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/ViewLogsFragment.kt @@ -33,9 +33,7 @@ class ViewLogsFragment : BaseFragment() { ): View { val view = inflater.inflate(R.layout.view_logs, container, false) - view.findViewById<View>(R.id.back).setOnClickListener { - activity?.onBackPressed() - } + view.findViewById<View>(R.id.back).setOnClickListener { activity?.onBackPressed() } logArea = view.findViewById<EditText>(R.id.log_area) @@ -46,9 +44,7 @@ class ViewLogsFragment : BaseFragment() { super.onStart() jobTracker.newUiJob("showLogs") { - val logs = jobTracker.runOnBackground { - problemReport.load() - } + val logs = jobTracker.runOnBackground { problemReport.load() } logArea.setText(logs) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt index e049afc7e5..a2176cceff 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/WelcomeFragment.kt @@ -49,8 +49,7 @@ class WelcomeFragment : BaseFragment() { private lateinit var headerBar: HeaderBar private lateinit var sitePaymentButton: SitePaymentButton - @Deprecated("Refactor code to instead rely on Lifecycle.") - private val jobTracker = JobTracker() + @Deprecated("Refactor code to instead rely on Lifecycle.") private val jobTracker = JobTracker() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -64,13 +63,15 @@ class WelcomeFragment : BaseFragment() { ): View? { val view = inflater.inflate(R.layout.welcome, container, false) - headerBar = view.findViewById<HeaderBar>(R.id.header_bar).apply { - tunnelState = TunnelState.Disconnected - } + headerBar = + view.findViewById<HeaderBar>(R.id.header_bar).apply { + tunnelState = TunnelState.Disconnected + } - accountLabel = view.findViewById<TextView>(R.id.account_number).apply { - setOnClickListener { copyAccountTokenToClipboard() } - } + accountLabel = + view.findViewById<TextView>(R.id.account_number).apply { + setOnClickListener { copyAccountTokenToClipboard() } + } view.findViewById<TextView>(R.id.pay_to_start_using).text = buildString { append(requireActivity().getString(R.string.pay_to_start_using)) @@ -78,17 +79,18 @@ class WelcomeFragment : BaseFragment() { append(requireActivity().getString(R.string.add_time_to_account)) } - sitePaymentButton = view.findViewById<SitePaymentButton>(R.id.site_payment).apply { - newAccount = true + sitePaymentButton = + view.findViewById<SitePaymentButton>(R.id.site_payment).apply { + newAccount = true - setOnClickAction("openAccountPageInBrowser", jobTracker) { - setEnabled(false) - serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> - context.openAccountPageInBrowser(token) + setOnClickAction("openAccountPageInBrowser", jobTracker) { + setEnabled(false) + serviceConnectionManager.authTokenCache()?.fetchAuthToken()?.let { token -> + context.openAccountPageInBrowser(token) + } + setEnabled(true) } - setEnabled(true) } - } view.findViewById<RedeemVoucherButton>(R.id.redeem_voucher).apply { prepare(parentFragmentManager, jobTracker) @@ -114,15 +116,11 @@ class WelcomeFragment : BaseFragment() { private fun CoroutineScope.launchUpdateAccountNumberOnDeviceChanges() = launch { deviceRepository.deviceState .debounce { it.addDebounceForUnknownState(UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS) } - .collect { state -> - updateAccountNumber(state.token()) - } + .collect { state -> updateAccountNumber(state.token()) } } private fun CoroutineScope.launchAdvanceToConnectViewOnExpiryExtended() = launch { - accountRepository.accountExpiryState.collect { - checkExpiry(it.date()) - } + accountRepository.accountExpiryState.collect { checkExpiry(it.date()) } } private fun CoroutineScope.launchExpiryPolling() = launch { @@ -136,9 +134,7 @@ class WelcomeFragment : BaseFragment() { serviceConnectionManager.connectionState .flatMapLatest { state -> if (state is ServiceConnectionState.ConnectedReady) { - callbackFlowFromNotifier( - state.container.connectionProxy.onStateChange - ) + callbackFlowFromNotifier(state.container.connectionProxy.onStateChange) } else { emptyFlow() } @@ -152,9 +148,7 @@ class WelcomeFragment : BaseFragment() { } private fun updateAccountNumber(rawAccountNumber: String?) { - val accountText = rawAccountNumber?.let { account -> - addSpacesToAccountText(account) - } + val accountText = rawAccountNumber?.let { account -> addSpacesToAccountText(account) } accountLabel.text = accountText ?: "" accountLabel.setEnabled(accountText != null && accountText.length > 0) @@ -168,12 +162,13 @@ class WelcomeFragment : BaseFragment() { } else { val numParts = (length - 1) / 4 + 1 - val parts = Array(numParts) { index -> - val startIndex = index * 4 - val endIndex = minOf(startIndex + 4, length) + val parts = + Array(numParts) { index -> + val startIndex = index * 4 + val endIndex = minOf(startIndex + 4, length) - account.substring(startIndex, endIndex) - } + account.substring(startIndex, endIndex) + } return parts.joinToString(" ") } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt index 63f19af27a..3966e0d7b9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ActionListItemView.kt @@ -11,7 +11,9 @@ import androidx.core.view.isVisible import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.WidgetState -open class ActionListItemView @JvmOverloads constructor( +open class ActionListItemView +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.actionListItemViewStyle, @@ -24,9 +26,7 @@ open class ActionListItemView @JvmOverloads constructor( protected val widgetContainer: ViewGroup = findViewById(R.id.widgetContainer) protected val clickListener = OnClickListener { - itemData.action?.let { _ -> - listItemListener?.onItemAction(itemData) - } + itemData.action?.let { _ -> listItemListener?.onItemAction(itemData) } } override val layoutRes: Int diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ApplicationListItemView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ApplicationListItemView.kt index 798d812802..138e9bfb88 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ApplicationListItemView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ApplicationListItemView.kt @@ -22,7 +22,9 @@ import org.koin.core.scope.Scope import org.koin.core.scope.inject @OptIn(KoinApiExtension::class) -class ApplicationListItemView @JvmOverloads constructor( +class ApplicationListItemView +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.applicationListItemViewStyle, @@ -43,17 +45,11 @@ class ApplicationListItemView @JvmOverloads constructor( override fun updateImage() { itemIcon.isVisible = true updateImageJob?.cancel() - updateImageJob = viewScope.launch { - loadImage()?.let { drawable -> - updateImage(drawable) - } - } + updateImageJob = viewScope.launch { loadImage()?.let { drawable -> updateImage(drawable) } } } override fun updateText() { - itemData.text?.let { - itemText.text = it - } + itemData.text?.let { itemText.text = it } } override fun onAttachedToWindow() { @@ -67,13 +63,14 @@ class ApplicationListItemView @JvmOverloads constructor( viewScope.coroutineContext.cancelChildren() } - private suspend fun loadImage(): Drawable? = withContext(Dispatchers.Default) { - try { - iconManager.getAppIcon(itemData.identifier) - } catch (e: PackageManager.NameNotFoundException) { - null + private suspend fun loadImage(): Drawable? = + withContext(Dispatchers.Default) { + try { + iconManager.getAppIcon(itemData.identifier) + } catch (e: PackageManager.NameNotFoundException) { + null + } } - } private fun updateImage(drawable: Drawable) = itemIcon.setImageDrawable(drawable) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt index 9baec663c5..5b8bb39b4b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/ListItemView.kt @@ -9,27 +9,28 @@ import androidx.constraintlayout.widget.ConstraintLayout import net.mullvad.mullvadvpn.model.ListItemData import net.mullvad.mullvadvpn.ui.ListItemListener -abstract class ListItemView @JvmOverloads constructor( +abstract class ListItemView +@JvmOverloads +constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) { - @get:LayoutRes - protected abstract val layoutRes: Int + @get:LayoutRes protected abstract val layoutRes: Int - @get:DimenRes - protected abstract val heightRes: Int? + @get:DimenRes protected abstract val heightRes: Int? protected lateinit var itemData: ListItemData var listItemListener: ListItemListener? = null init { val view = LayoutInflater.from(context).inflate(layoutRes, this, true) - val height = if (heightRes != null) { - resources.getDimensionPixelSize(heightRes!!) - } else { - LayoutParams.WRAP_CONTENT - } + val height = + if (heightRes != null) { + resources.getDimensionPixelSize(heightRes!!) + } else { + LayoutParams.WRAP_CONTENT + } view.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, height) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt index f1ec04bd1d..73076fbad8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/listitemview/WidgetViewController.kt @@ -9,8 +9,7 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.model.WidgetState sealed class WidgetViewController<T : WidgetState>(val parent: ViewGroup) { - @get:LayoutRes - protected abstract val layoutRes: Int + @get:LayoutRes protected abstract val layoutRes: Int init { LayoutInflater.from(parent.context).inflate(layoutRes, parent) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/InAppNotificationController.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/InAppNotificationController.kt index 1c0fb73d93..09b4947bbc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/InAppNotificationController.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/InAppNotificationController.kt @@ -12,11 +12,12 @@ class InAppNotificationController(private val onNotificationChanged: (InAppNotif private val activeNotifications = PriorityQueue(notificationPrioritizer) private val notifications = HashMap<InAppNotification, Int>() - var current by observable<InAppNotification?>(null) { _, oldNotification, newNotification -> - if (oldNotification != newNotification) { - onNotificationChanged.invoke(newNotification) + var current by + observable<InAppNotification?>(null) { _, oldNotification, newNotification -> + if (oldNotification != newNotification) { + onNotificationChanged.invoke(newNotification) + } } - } fun register(notification: InAppNotification) { notification.controller = this diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrl.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrl.kt index 8e03db6df5..c73ccd2a6b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrl.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/NotificationWithUrl.kt @@ -5,16 +5,12 @@ import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.net.Uri -abstract class NotificationWithUrl( - protected val context: Context, - urlId: Int -) : InAppNotification() { +abstract class NotificationWithUrl(protected val context: Context, urlId: Int) : + InAppNotification() { private val url = Uri.parse(context.getString(urlId)) protected val openUrl: suspend () -> Unit = { - val intent = Intent(Intent.ACTION_VIEW, url).apply { - flags = FLAG_ACTIVITY_NEW_TASK - } + val intent = Intent(Intent.ACTION_VIEW, url).apply { flags = FLAG_ACTIVITY_NEW_TASK } context.startActivity(intent) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/VersionInfoNotification.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/VersionInfoNotification.kt index d85f70ca5b..ad00489f56 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/VersionInfoNotification.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/notification/VersionInfoNotification.kt @@ -4,9 +4,8 @@ import android.content.Context import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.ui.VersionInfo -class VersionInfoNotification( - context: Context -) : NotificationWithUrl(context, R.string.download_url) { +class VersionInfoNotification(context: Context) : + NotificationWithUrl(context, R.string.download_url) { private val unsupportedVersion = context.getString(R.string.unsupported_version) private val updateAvailable = context.getString(R.string.update_available) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AppVersionInfoCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AppVersionInfoCache.kt index b921063c24..5280ef472d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AppVersionInfoCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AppVersionInfoCache.kt @@ -9,9 +9,8 @@ class AppVersionInfoCache( eventDispatcher: EventDispatcher, private val settingsListener: SettingsListener ) { - private var appVersionInfo by observable<AppVersionInfo?>(null) { _, _, _ -> - onUpdate?.invoke() - } + private var appVersionInfo by + observable<AppVersionInfo?>(null) { _, _, _ -> onUpdate?.invoke() } val isSupported get() = appVersionInfo?.supported ?: true @@ -22,15 +21,14 @@ class AppVersionInfoCache( val upgradeVersion get() = appVersionInfo?.suggestedUpgrade - var onUpdate by observable<(() -> Unit)?>(null) { _, _, callback -> - callback?.invoke() - } + var onUpdate by observable<(() -> Unit)?>(null) { _, _, callback -> callback?.invoke() } - var showBetaReleases by observable(false) { _, wasShowing, shouldShow -> - if (shouldShow != wasShowing) { - onUpdate?.invoke() + var showBetaReleases by + observable(false) { _, wasShowing, shouldShow -> + if (shouldShow != wasShowing) { + onUpdate?.invoke() + } } - } private set var version: String? = null @@ -38,9 +36,7 @@ class AppVersionInfoCache( init { eventDispatcher.apply { - registerHandler(Event.CurrentVersion::class) { event -> - version = event.version - } + registerHandler(Event.CurrentVersion::class) { event -> version = event.version } registerHandler(Event.AppVersionInfo::class) { event -> appVersionInfo = event.versionInfo @@ -48,9 +44,7 @@ class AppVersionInfoCache( } settingsListener.settingsNotifier.subscribe(this) { maybeSettings -> - maybeSettings?.let { settings -> - showBetaReleases = settings.showBetaReleases - } + maybeSettings?.let { settings -> showBetaReleases = settings.showBetaReleases } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt index 2078de671a..0bef217ca2 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/AuthTokenCache.kt @@ -12,18 +12,14 @@ class AuthTokenCache(private val connection: Messenger, eventDispatcher: EventDi init { eventDispatcher.registerHandler(Event.AuthToken::class) { event -> - synchronized(this@AuthTokenCache) { - fetchQueue.poll()?.complete(event.token ?: "") - } + synchronized(this@AuthTokenCache) { fetchQueue.poll()?.complete(event.token ?: "") } } } suspend fun fetchAuthToken(): String { val authToken = CompletableDeferred<String>() - synchronized(this) { - fetchQueue.offer(authToken) - } + synchronized(this) { fetchQueue.offer(authToken) } connection.send(Request.FetchAuthToken.message) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt index fcd8565550..33a1ffb4e9 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ConnectionProxy.kt @@ -82,19 +82,20 @@ class ConnectionProxy(private val connection: Messenger, eventDispatcher: EventD synchronized(this) { val currentState = uiState - val willReconnect = when (currentState) { - is TunnelState.Disconnected -> false - is TunnelState.Disconnecting -> { - when (currentState.actionAfterDisconnect) { - ActionAfterDisconnect.Nothing -> false - ActionAfterDisconnect.Reconnect -> true - ActionAfterDisconnect.Block -> true + val willReconnect = + when (currentState) { + is TunnelState.Disconnected -> false + is TunnelState.Disconnecting -> { + when (currentState.actionAfterDisconnect) { + ActionAfterDisconnect.Nothing -> false + ActionAfterDisconnect.Reconnect -> true + ActionAfterDisconnect.Block -> true + } } + is TunnelState.Connecting -> true + is TunnelState.Connected -> true + is TunnelState.Error -> true } - is TunnelState.Connecting -> true - is TunnelState.Connected -> true - is TunnelState.Error -> true - } if (willReconnect) { scheduleToResetAnticipatedState() @@ -124,15 +125,16 @@ class ConnectionProxy(private val connection: Messenger, eventDispatcher: EventD var currentJob: Job? = null - val newJob = GlobalScope.launch(Dispatchers.Default) { - delay(ANTICIPATED_STATE_TIMEOUT_MS) + val newJob = + GlobalScope.launch(Dispatchers.Default) { + delay(ANTICIPATED_STATE_TIMEOUT_MS) - synchronized(this@ConnectionProxy) { - if (!currentJob!!.isCancelled) { - uiState = state + synchronized(this@ConnectionProxy) { + if (!currentJob!!.isCancelled) { + uiState = state + } } } - } currentJob = newJob resetAnticipatedStateJob = newJob diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/LocationInfoCache.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/LocationInfoCache.kt index 8eee6503c7..198a61cd59 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/LocationInfoCache.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/LocationInfoCache.kt @@ -6,13 +6,13 @@ import net.mullvad.mullvadvpn.ipc.EventDispatcher import net.mullvad.mullvadvpn.model.GeoIpLocation class LocationInfoCache(eventDispatcher: EventDispatcher) { - private var location: GeoIpLocation? by observable(null) { _, _, newLocation -> - onNewLocation?.invoke(newLocation) - } + private var location: GeoIpLocation? by + observable(null) { _, _, newLocation -> onNewLocation?.invoke(newLocation) } - var onNewLocation by observable<((GeoIpLocation?) -> Unit)?>(null) { _, _, callback -> - callback?.invoke(location) - } + var onNewLocation by + observable<((GeoIpLocation?) -> Unit)?>(null) { _, _, callback -> + callback?.invoke(location) + } init { eventDispatcher.registerHandler(Event.NewLocation::class) { event -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt index aebe1f0a4f..5a92eea719 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/RelayListListener.kt @@ -48,9 +48,7 @@ class RelayListListener( init { eventDispatcher.registerHandler(Event.NewRelayList::class) { event -> - event.relayList?.let { relayLocations -> - relayListChanged(RelayList(relayLocations)) - } + event.relayList?.let { relayLocations -> relayListChanged(RelayList(relayLocations)) } } settingsListener.relaySettingsNotifier.subscribe(this) { newRelaySettings -> @@ -67,8 +65,8 @@ class RelayListListener( synchronized(this) { val relayList = this.relayList - relaySettings = newRelaySettings - ?: RelaySettings.Normal(RelayConstraints(Constraint.Any())) + relaySettings = + newRelaySettings ?: RelaySettings.Normal(RelayConstraints(Constraint.Any())) if (relayList != null) { relayListChanged(relayList) @@ -95,7 +93,9 @@ class RelayListListener( return relayList?.findItemForLocation(location, true) } - else -> { /* NOOP */ } + else -> { + /* NOOP */ + } } return null diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionAccountDataSource.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionAccountDataSource.kt index 05f6740a42..ff01f20eae 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionAccountDataSource.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionAccountDataSource.kt @@ -12,9 +12,7 @@ class ServiceConnectionAccountDataSource( private val dispatcher: EventDispatcher ) { val accountCreationResult = callbackFlow { - val handler: (Event.AccountCreationEvent) -> Unit = { event -> - trySend(event.result) - } + val handler: (Event.AccountCreationEvent) -> Unit = { event -> trySend(event.result) } dispatcher.registerHandler(Event.AccountCreationEvent::class, handler) awaitClose { // The current dispatcher doesn't support unregistration of handlers. @@ -22,9 +20,7 @@ class ServiceConnectionAccountDataSource( } val accountExpiry = callbackFlow { - val handler: (Event.AccountExpiryEvent) -> Unit = { event -> - trySend(event.expiry) - } + val handler: (Event.AccountExpiryEvent) -> Unit = { event -> trySend(event.expiry) } dispatcher.registerHandler(Event.AccountExpiryEvent::class, handler) connection.send(Request.FetchAccountExpiry.message) awaitClose { @@ -33,9 +29,7 @@ class ServiceConnectionAccountDataSource( } val accountHistory = callbackFlow { - val handler: (Event.AccountHistoryEvent) -> Unit = { event -> - trySend(event.history) - } + val handler: (Event.AccountHistoryEvent) -> Unit = { event -> trySend(event.history) } dispatcher.registerHandler(Event.AccountHistoryEvent::class, handler) awaitClose { // The current dispatcher doesn't support unregistration of handlers. @@ -43,9 +37,7 @@ class ServiceConnectionAccountDataSource( } val loginEvents = callbackFlow { - val handler: (Event.LoginEvent) -> Unit = { event -> - trySend(event) - } + val handler: (Event.LoginEvent) -> Unit = { event -> trySend(event) } dispatcher.registerHandler(Event.LoginEvent::class, handler) awaitClose { // The current dispatcher doesn't support unregistration of handlers. diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt index 130dc1a8c9..a58db46ff7 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionContainer.kt @@ -24,14 +24,11 @@ class ServiceConnectionContainer( onServiceReady: (ServiceConnectionContainer) -> Unit, onVpnPermissionRequest: () -> Unit ) : KoinScopeComponent { - private val dispatcher = DispatchingHandler(Looper.getMainLooper()) { message -> - Event.fromMessage(message) - } + private val dispatcher = + DispatchingHandler(Looper.getMainLooper()) { message -> Event.fromMessage(message) } - override val scope = getKoin().getOrCreateScope( - SERVICE_CONNECTION_SCOPE, - named(SERVICE_CONNECTION_SCOPE), this - ) + override val scope = + getKoin().getOrCreateScope(SERVICE_CONNECTION_SCOPE, named(SERVICE_CONNECTION_SCOPE), this) val accountDataSource = ServiceConnectionAccountDataSource(connection, dispatcher) val authTokenCache = AuthTokenCache(connection, dispatcher) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt index 39159a1a17..2f4ee1613c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionDeviceDataSource.kt @@ -13,9 +13,7 @@ class ServiceConnectionDeviceDataSource( private val dispatcher: EventDispatcher ) { val deviceStateUpdates = callbackFlow { - val handler: (Event.DeviceStateEvent) -> Unit = { event -> - trySend(event.newState) - } + val handler: (Event.DeviceStateEvent) -> Unit = { event -> trySend(event.newState) } dispatcher.registerHandler(Event.DeviceStateEvent::class, handler) connection.trySendRequest(Request.GetDevice, false) awaitClose { @@ -24,9 +22,7 @@ class ServiceConnectionDeviceDataSource( } val deviceListUpdates = callbackFlow { - val handler: (Event.DeviceListUpdate) -> Unit = { event -> - trySend(event.event) - } + val handler: (Event.DeviceListUpdate) -> Unit = { event -> trySend(event.event) } dispatcher.registerHandler(Event.DeviceListUpdate::class, handler) awaitClose { // The current dispatcher doesn't support unregistration of handlers. @@ -34,9 +30,7 @@ class ServiceConnectionDeviceDataSource( } val deviceRemovalResult = callbackFlow { - val handler: (Event.DeviceRemovalEvent) -> Unit = { event -> - trySend(event) - } + val handler: (Event.DeviceRemovalEvent) -> Unit = { event -> trySend(event) } dispatcher.registerHandler(Event.DeviceRemovalEvent::class, handler) awaitClose { // The current dispatcher doesn't support unregistration of handlers. diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt index a2a6a63f03..0bbc4e71d4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManager.kt @@ -14,9 +14,7 @@ import net.mullvad.mullvadvpn.lib.endpoint.putApiEndpointConfigurationExtra import net.mullvad.mullvadvpn.service.MullvadVpnService import net.mullvad.talpid.util.EventNotifier -class ServiceConnectionManager( - private val context: Context -) { +class ServiceConnectionManager(private val context: Context) { private val _connectionState = MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) @@ -29,27 +27,28 @@ class ServiceConnectionManager( var isBound = false private var vpnPermissionRequestHandler: (() -> Unit)? = null - private val serviceConnection = object : android.content.ServiceConnection { - override fun onServiceConnected(className: ComponentName, binder: IBinder) { - Log.d("mullvad", "UI successfully connected to the service") + private val serviceConnection = + object : android.content.ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + Log.d("mullvad", "UI successfully connected to the service") - notify( - ServiceConnectionState.ConnectedNotReady( - ServiceConnectionContainer( - Messenger(binder), - ::handleNewServiceConnection, - ::handleVpnPermissionRequest + notify( + ServiceConnectionState.ConnectedNotReady( + ServiceConnectionContainer( + Messenger(binder), + ::handleNewServiceConnection, + ::handleVpnPermissionRequest + ) ) ) - ) - } + } - override fun onServiceDisconnected(className: ComponentName) { - Log.d("mullvad", "UI lost the connection to the service") - _connectionState.value.readyContainer()?.onDestroy() - notify(ServiceConnectionState.Disconnected) + override fun onServiceDisconnected(className: ComponentName) { + Log.d("mullvad", "UI lost the connection to the service") + _connectionState.value.readyContainer()?.onDestroy() + notify(ServiceConnectionState.Disconnected) + } } - } fun bind( vpnPermissionRequestHandler: () -> Unit, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt index 392b841101..1de160ffe8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/ServiceConnectionManagerExtensions.kt @@ -15,8 +15,7 @@ fun ServiceConnectionManager.connectionProxy() = fun ServiceConnectionManager.deviceDataSource() = this.connectionState.value.readyContainer()?.deviceDataSource -fun ServiceConnectionManager.customDns() = - this.connectionState.value.readyContainer()?.customDns +fun ServiceConnectionManager.customDns() = this.connectionState.value.readyContainer()?.customDns fun ServiceConnectionManager.relayListListener() = this.connectionState.value.readyContainer()?.relayListListener diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SplitTunneling.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SplitTunneling.kt index 7800661b21..877d847abc 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SplitTunneling.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/SplitTunneling.kt @@ -9,11 +9,12 @@ import net.mullvad.mullvadvpn.ipc.Request class SplitTunneling(private val connection: Messenger, eventDispatcher: EventDispatcher) { private var excludedApps: Set<String> = emptySet() - var enabled by observable(false) { _, wasEnabled, isEnabled -> - if (wasEnabled != isEnabled) { - connection.send(Request.SetEnableSplitTunneling(isEnabled).message) + var enabled by + observable(false) { _, wasEnabled, isEnabled -> + if (wasEnabled != isEnabled) { + connection.send(Request.SetEnableSplitTunneling(isEnabled).message) + } } - } init { eventDispatcher.registerHandler(Event.SplitTunnelingUpdate::class) { event -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VoucherRedeemer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VoucherRedeemer.kt index d2378100ea..bb538f3f6b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VoucherRedeemer.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/serviceconnection/VoucherRedeemer.kt @@ -22,9 +22,7 @@ class VoucherRedeemer(val connection: Messenger, eventDispatcher: MessageDispatc suspend fun submit(voucher: String): VoucherSubmissionResult { val result = CompletableDeferred<VoucherSubmissionResult>() - synchronized(this) { - activeSubmissions.put(voucher, result) - } + synchronized(this) { activeSubmissions.put(voucher, result) } connection.send(Request.SubmitVoucher(voucher).message) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt index 8ead4f917e..4a4acf737a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountCell.kt @@ -18,46 +18,51 @@ class AccountCell : NavigateCell { private val expiredColor = context.getColor(R.color.red) private val normalColor = context.getColor(R.color.white60) - private val remainingTimeLabel = TextView(context).apply { - layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) - gravity = Gravity.RIGHT + private val remainingTimeLabel = + TextView(context).apply { + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) + gravity = Gravity.RIGHT - resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> - setPadding(padding, 0, padding, 0) - } + resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> + setPadding(padding, 0, padding, 0) + } - setAllCaps(true) - setTextColor(normalColor) - setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_small)) - setTypeface(null, Typeface.BOLD) + setAllCaps(true) + setTextColor(normalColor) + setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_small)) + setTypeface(null, Typeface.BOLD) - text = "" - } + text = "" + } - var accountExpiry by observable<DateTime?>(null) { _, _, expiry -> - remainingTimeLabel.apply { - if (expiry != null) { - val remainingTime = Duration(DateTime.now(), expiry) + var accountExpiry by + observable<DateTime?>(null) { _, _, expiry -> + remainingTimeLabel.apply { + if (expiry != null) { + val remainingTime = Duration(DateTime.now(), expiry) - if (remainingTime.isShorterThan(Duration.ZERO)) { - setText(R.string.out_of_time) - setTextColor(expiredColor) + if (remainingTime.isShorterThan(Duration.ZERO)) { + setText(R.string.out_of_time) + setTextColor(expiredColor) + } else { + setText(formatter.format(expiry, remainingTime)) + setTextColor(normalColor) + } } else { - setText(formatter.format(expiry, remainingTime)) - setTextColor(normalColor) + text = "" } - } else { - text = "" } } - } constructor(context: Context) : super(context) constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { cell.addView(remainingTimeLabel, cell.childCount - 1) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryAdapter.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryAdapter.kt index 48a05dd63e..e60d9c406f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryAdapter.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryAdapter.kt @@ -8,15 +8,12 @@ import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.util.SegmentedTextFormatter class AccountHistoryAdapter : Adapter<AccountHistoryHolder>() { - private val formatter = SegmentedTextFormatter(' ').apply { - isValidInputCharacter = { character -> - '0' <= character && character <= '9' + private val formatter = + SegmentedTextFormatter(' ').apply { + isValidInputCharacter = { character -> '0' <= character && character <= '9' } } - } - var accountHistory by observable<String?>(null) { _, _, _ -> - notifyDataSetChanged() - } + var accountHistory by observable<String?>(null) { _, _, _ -> notifyDataSetChanged() } var onSelectEntry: ((String) -> Unit)? = null var onRemoveEntry: (() -> Unit)? = null diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryHolder.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryHolder.kt index 4a87f4f601..20685a0ca3 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryHolder.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountHistoryHolder.kt @@ -7,15 +7,11 @@ import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.R import net.mullvad.mullvadvpn.util.SegmentedTextFormatter -class AccountHistoryHolder( - view: View, - private val formatter: SegmentedTextFormatter -) : ViewHolder(view) { +class AccountHistoryHolder(view: View, private val formatter: SegmentedTextFormatter) : + ViewHolder(view) { private val label: TextView = view.findViewById(R.id.label) - var accountToken by observable("") { _, _, account -> - label.text = formatter.format(account) - } + var accountToken by observable("") { _, _, account -> label.text = formatter.format(account) } var onSelect: ((String) -> Unit)? = null var onRemove: ((String) -> Unit)? = null @@ -23,9 +19,7 @@ class AccountHistoryHolder( init { view.findViewById<View>(R.id.remove).apply { - setOnClickListener { - onRemove?.invoke(accountToken) - } + setOnClickListener { onRemove?.invoke(accountToken) } setOnFocusChangeListener { _, hasFocus -> onFocusChanged?.invoke(accountToken, hasFocus) @@ -33,9 +27,7 @@ class AccountHistoryHolder( } label.apply { - setOnClickListener { - onSelect?.invoke(accountToken) - } + setOnClickListener { onSelect?.invoke(accountToken) } setOnFocusChangeListener { _, hasFocus -> onFocusChanged?.invoke(accountToken, hasFocus) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountInput.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountInput.kt index 1312e4ddfd..32e81a25e4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountInput.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountInput.kt @@ -33,54 +33,62 @@ class AccountInput : LinearLayout { inflater.inflate(R.layout.account_input, this) } - private val inputWatcher = object : TextWatcher { - override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {} + private val inputWatcher = + object : TextWatcher { + override fun beforeTextChanged( + text: CharSequence, + start: Int, + count: Int, + after: Int + ) {} - override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {} + override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {} - override fun afterTextChanged(text: Editable) { - removeFormattingSpans(text) - setButtonEnabled(text.length >= MIN_ACCOUNT_TOKEN_LENGTH) - onTextChanged.notify(text.toString()) + override fun afterTextChanged(text: Editable) { + removeFormattingSpans(text) + setButtonEnabled(text.length >= MIN_ACCOUNT_TOKEN_LENGTH) + onTextChanged.notify(text.toString()) + } } - } - val input = container.findViewById<EditText>(R.id.login_input).apply { - addTextChangedListener(inputWatcher) - setOnEnterOrDoneAction(::login) + val input = + container.findViewById<EditText>(R.id.login_input).apply { + addTextChangedListener(inputWatcher) + setOnEnterOrDoneAction(::login) - onFocusChangeListener = OnFocusChangeListener { view, inputHasFocus -> - hasFocus = inputHasFocus && view.isEnabled - } + onFocusChangeListener = OnFocusChangeListener { view, inputHasFocus -> + hasFocus = inputHasFocus && view.isEnabled + } - // Manually initializing the `DigitsKeyListener` allows spaces to be used and still keeps - // the input type as a number so that the correct software keyboard type is shown - keyListener = DigitsKeyListener.getInstance("01234567890 ") + // Manually initializing the `DigitsKeyListener` allows spaces to be used and still + // keeps + // the input type as a number so that the correct software keyboard type is shown + keyListener = DigitsKeyListener.getInstance("01234567890 ") - SegmentedInputFormatter(this, ' ').apply { - isValidInputCharacter = { character -> - '0' <= character && character <= '9' + SegmentedInputFormatter(this, ' ').apply { + isValidInputCharacter = { character -> '0' <= character && character <= '9' } } } - } - private val button = container.findViewById<ImageButton>(R.id.login_button).apply { - setOnClickListener { login() } - } + private val button = + container.findViewById<ImageButton>(R.id.login_button).apply { + setOnClickListener { login() } + } val onFocusChanged = EventNotifier(false) private var hasFocus by onFocusChanged.notifiable() val onTextChanged = EventNotifier("") - var loginState by observable(LoginState.Initial) { _, _, state -> - when (state) { - LoginState.Initial -> initialState() - LoginState.InProgress -> loggingInState() - LoginState.Success -> successState() - LoginState.Failure -> failureState() + var loginState by + observable(LoginState.Initial) { _, _, state -> + when (state) { + LoginState.Initial -> initialState() + LoginState.InProgress -> loggingInState() + LoginState.Success -> successState() + LoginState.Failure -> failureState() + } } - } var onLogin: ((String) -> Unit)? = null @@ -88,8 +96,11 @@ class AccountInput : LinearLayout { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { orientation = HORIZONTAL diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLogin.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLogin.kt index c204445168..82dc8b6451 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLogin.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLogin.kt @@ -22,12 +22,13 @@ class AccountLogin : RelativeLayout { private val MAX_ACCOUNT_HISTORY_ENTRIES = 3 } - fun setAccountToken(accountToken: String) { input.input.setText(accountToken) } - - private val focusDebouncer = Debouncer(false).apply { - listener = { hasFocus -> focused = hasFocus } + fun setAccountToken(accountToken: String) { + input.input.setText(accountToken) } + private val focusDebouncer = + Debouncer(false).apply { listener = { hasFocus -> focused = hasFocus } } + private val container = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE).let { service -> val inflater = service as LayoutInflater @@ -39,22 +40,22 @@ class AccountLogin : RelativeLayout { private val accountHistoryList: RecyclerView = container.findViewById(R.id.history) private val input: AccountInput = container.findViewById(R.id.input) - private val historyAdapter = AccountHistoryAdapter().apply { - onSelectEntry = { account -> input.loginWith(account) } - onChildFocusChanged = { _, hasFocus -> focusDebouncer.rawValue = hasFocus } - } + private val historyAdapter = + AccountHistoryAdapter().apply { + onSelectEntry = { account -> input.loginWith(account) } + onChildFocusChanged = { _, hasFocus -> focusDebouncer.rawValue = hasFocus } + } private val dividerHeight = resources.getDimensionPixelSize(R.dimen.account_history_divider) private val historyEntryHeight = resources.getDimensionPixelSize(R.dimen.account_history_entry_height) - private val historyAnimation = ValueAnimator.ofInt(0, 0).apply { - addUpdateListener { animation -> - updateHeight(animation.animatedValue as Int) - } + private val historyAnimation = + ValueAnimator.ofInt(0, 0).apply { + addUpdateListener { animation -> updateHeight(animation.animatedValue as Int) } - duration = 350 - } + duration = 350 + } private val maxHeight: Int get() = MAX_ACCOUNT_HISTORY_ENTRIES * (historyEntryHeight + dividerHeight) @@ -62,58 +63,65 @@ class AccountLogin : RelativeLayout { private val expandedHeight: Int get() = collapsedHeight + (historyHeight ?: 0) - private var historyHeight by observable<Int?>(null) { _, oldHistoryHeight, newHistoryHeight -> - if (newHistoryHeight != oldHistoryHeight) { - historyAnimation.setIntValues(collapsedHeight, expandedHeight) - reposition() + private var historyHeight by + observable<Int?>(null) { _, oldHistoryHeight, newHistoryHeight -> + if (newHistoryHeight != oldHistoryHeight) { + historyAnimation.setIntValues(collapsedHeight, expandedHeight) + reposition() + } } - } - private var collapsedHeight by observable( - resources.getDimensionPixelSize(R.dimen.account_login_input_height) - ) { _, oldCollapsedHeight, newCollapsedHeight -> - if (newCollapsedHeight != oldCollapsedHeight) { - historyAnimation.setIntValues(newCollapsedHeight, expandedHeight) - reposition() + private var collapsedHeight by + observable(resources.getDimensionPixelSize(R.dimen.account_login_input_height)) { + _, + oldCollapsedHeight, + newCollapsedHeight -> + if (newCollapsedHeight != oldCollapsedHeight) { + historyAnimation.setIntValues(newCollapsedHeight, expandedHeight) + reposition() + } } - } - private var focused by observable(false) { _, _, hasFocus -> - updateBorder() - shouldShowAccountHistory = hasFocus + private var focused by + observable(false) { _, _, hasFocus -> + updateBorder() + shouldShowAccountHistory = hasFocus - if (!hasFocus) { - hideKeyboard() + if (!hasFocus) { + hideKeyboard() + } } - } - private var shouldShowAccountHistory by observable(false) { _, isShown, show -> - if (isShown != show) { - if (show) { - historyAnimation.start() - } else { - historyAnimation.reverse() + private var shouldShowAccountHistory by + observable(false) { _, isShown, show -> + if (isShown != show) { + if (show) { + historyAnimation.start() + } else { + historyAnimation.reverse() + } } } - } val hasFocus get() = focused - var accountHistory by observable<String?>(null) { _, _, history -> - if (history != null) { - historyHeight = historyEntryHeight + dividerHeight - historyAdapter.accountHistory = history - } else { - historyHeight = 0 + var accountHistory by + observable<String?>(null) { _, _, history -> + if (history != null) { + historyHeight = historyEntryHeight + dividerHeight + historyAdapter.accountHistory = history + } else { + historyHeight = 0 + } } - } - var state: LoginState by observable(LoginState.Initial) { _, _, newState -> - input.loginState = newState + var state: LoginState by + observable(LoginState.Initial) { _, _, newState -> + input.loginState = newState - updateBorder() - } + updateBorder() + } var onLogin: ((String) -> Unit)? get() = input.onLogin @@ -131,16 +139,17 @@ class AccountLogin : RelativeLayout { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { border.elevation = elevation + 0.1f input.apply { - onFocusChanged.subscribe(this) { hasFocus -> - focusDebouncer.rawValue = hasFocus - } + onFocusChanged.subscribe(this) { hasFocus -> focusDebouncer.rawValue = hasFocus } onTextChanged.subscribe(this) { _ -> if (state == LoginState.Failure) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLoginBorder.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLoginBorder.kt index 4f2a384c98..c728941264 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLoginBorder.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AccountLoginBorder.kt @@ -26,23 +26,26 @@ class AccountLoginBorder : RelativeLayout { val verticalBorder: Drawable ) - private val unfocusedDrawables = StateDrawables( - resources.getDrawable(R.drawable.account_login_corner, null), - resources.getDrawable(R.drawable.account_login_border, null), - resources.getDrawable(R.drawable.account_login_border, null) - ) + private val unfocusedDrawables = + StateDrawables( + resources.getDrawable(R.drawable.account_login_corner, null), + resources.getDrawable(R.drawable.account_login_border, null), + resources.getDrawable(R.drawable.account_login_border, null) + ) - private val focusedDrawables = StateDrawables( - resources.getDrawable(R.drawable.account_login_corner_focused, null), - resources.getDrawable(R.drawable.account_login_border_focused, null), - resources.getDrawable(R.drawable.account_login_border_focused, null) - ) + private val focusedDrawables = + StateDrawables( + resources.getDrawable(R.drawable.account_login_corner_focused, null), + resources.getDrawable(R.drawable.account_login_border_focused, null), + resources.getDrawable(R.drawable.account_login_border_focused, null) + ) - private val errorDrawables = StateDrawables( - resources.getDrawable(R.drawable.account_login_corner_error, null), - resources.getDrawable(R.drawable.account_login_border_error, null), - resources.getDrawable(R.drawable.account_login_border_error, null) - ) + private val errorDrawables = + StateDrawables( + resources.getDrawable(R.drawable.account_login_corner_error, null), + resources.getDrawable(R.drawable.account_login_border_error, null), + resources.getDrawable(R.drawable.account_login_border_error, null) + ) private val container = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE).let { service -> @@ -76,8 +79,11 @@ class AccountLoginBorder : RelativeLayout { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) constructor( context: Context, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt index 06dad0ac39..cb646a5e26 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/AppVersionCell.kt @@ -12,46 +12,47 @@ import kotlin.properties.Delegates.observable import net.mullvad.mullvadvpn.R class AppVersionCell : UrlCell { - private val warningIcon = ImageView(context).apply { - val iconSize = resources.getDimensionPixelSize(R.dimen.app_version_warning_icon_size) + private val warningIcon = + ImageView(context).apply { + val iconSize = resources.getDimensionPixelSize(R.dimen.app_version_warning_icon_size) - layoutParams = LayoutParams(iconSize, iconSize, 0.0f) + layoutParams = LayoutParams(iconSize, iconSize, 0.0f) - resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> - setPadding(0, 0, padding, 0) - } + resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> + setPadding(0, 0, padding, 0) + } - setImageResource(R.drawable.icon_alert) - } + setImageResource(R.drawable.icon_alert) + } - private val versionLabel = TextView(context).apply { - layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) - gravity = Gravity.RIGHT + private val versionLabel = + TextView(context).apply { + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) + gravity = Gravity.RIGHT - resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> - setPadding(padding, 0, padding, 0) - } + resources.getDimensionPixelSize(R.dimen.cell_inner_spacing).let { padding -> + setPadding(padding, 0, padding, 0) + } - setTextColor(context.getColor(R.color.white60)) - setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_small)) - setTypeface(null, Typeface.BOLD) + setTextColor(context.getColor(R.color.white60)) + setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_small)) + setTypeface(null, Typeface.BOLD) - text = "" - } + text = "" + } - var updateAvailable by observable(false) { _, _, updateAvailable -> - if (updateAvailable) { - warningIcon.visibility = VISIBLE - footer?.visibility = VISIBLE - } else { - warningIcon.visibility = GONE - footer?.visibility = GONE + var updateAvailable by + observable(false) { _, _, updateAvailable -> + if (updateAvailable) { + warningIcon.visibility = VISIBLE + footer?.visibility = VISIBLE + } else { + warningIcon.visibility = GONE + footer?.visibility = GONE + } } - } - var version by observable("") { _, _, version -> - versionLabel.text = version - } + var version by observable("") { _, _, version -> versionLabel.text = version } @JvmOverloads constructor( diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/BackButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/BackButton.kt index d94fa31ec4..2bc4579c11 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/BackButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/BackButton.kt @@ -25,8 +25,11 @@ class BackButton : LinearLayout { loadAttributes(attributes) } - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) { + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) { loadAttributes(attributes) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt index c2596b04d6..8a014a7ded 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Button.kt @@ -48,11 +48,12 @@ open class Button : FrameLayout { set(value) { field = value - val backgroundResource = when (value) { - ButtonColor.Blue -> R.drawable.blue_button_background - ButtonColor.Green -> R.drawable.green_button_background - ButtonColor.Red -> R.drawable.red_button_background - } + val backgroundResource = + when (value) { + ButtonColor.Blue -> R.drawable.blue_button_background + ButtonColor.Green -> R.drawable.green_button_background + ButtonColor.Red -> R.drawable.red_button_background + } button.setBackgroundResource(backgroundResource) } @@ -85,8 +86,11 @@ open class Button : FrameLayout { loadAttributes(attributes) } - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) { + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) { loadAttributes(attributes) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt index 54f35fd519..b78c9bc14c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/Cell.kt @@ -10,53 +10,65 @@ import android.widget.TextView import net.mullvad.mullvadvpn.R open class Cell : LinearLayout { - private val label = TextView(context).apply { - val rightPadding = resources.getDimensionPixelSize(R.dimen.cell_inner_spacing) - val verticalPadding = resources.getDimensionPixelSize(R.dimen.cell_label_vertical_padding) + private val label = + TextView(context).apply { + val rightPadding = resources.getDimensionPixelSize(R.dimen.cell_inner_spacing) + val verticalPadding = + resources.getDimensionPixelSize(R.dimen.cell_label_vertical_padding) - layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f) - setPadding(0, verticalPadding, rightPadding, verticalPadding) + layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1.0f) + setPadding(0, verticalPadding, rightPadding, verticalPadding) - setTextColor(context.getColor(R.color.white)) - setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_medium_plus)) - setTypeface(null, Typeface.BOLD) - } + setTextColor(context.getColor(R.color.white)) + setTextSize( + TypedValue.COMPLEX_UNIT_PX, + resources.getDimension(R.dimen.text_medium_plus) + ) + setTypeface(null, Typeface.BOLD) + } protected var footer: TextView? = null set(value) { - field = value?.apply { - val horizontalPadding = - resources.getDimensionPixelSize(R.dimen.cell_footer_horizontal_padding) - val topPadding = resources.getDimensionPixelSize(R.dimen.cell_footer_top_padding) + field = + value?.apply { + val horizontalPadding = + resources.getDimensionPixelSize(R.dimen.cell_footer_horizontal_padding) + val topPadding = + resources.getDimensionPixelSize(R.dimen.cell_footer_top_padding) - layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - setPadding(horizontalPadding, topPadding, horizontalPadding, 0) + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + setPadding(horizontalPadding, topPadding, horizontalPadding, 0) - setTextColor(context.getColor(R.color.white60)) - setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(R.dimen.text_small)) - } + setTextColor(context.getColor(R.color.white60)) + setTextSize( + TypedValue.COMPLEX_UNIT_PX, + resources.getDimension(R.dimen.text_small) + ) + } } protected var cell: LinearLayout = this set(value) { - field = value.apply { - val height = resources.getDimensionPixelSize(R.dimen.cell_height) - val leftPadding = resources.getDimensionPixelSize(R.dimen.cell_left_padding) - val rightPadding = resources.getDimensionPixelSize(R.dimen.cell_right_padding) + field = + value.apply { + val height = resources.getDimensionPixelSize(R.dimen.cell_height) + val leftPadding = resources.getDimensionPixelSize(R.dimen.cell_left_padding) + val rightPadding = resources.getDimensionPixelSize(R.dimen.cell_right_padding) - setFocusable(true) - isClickable = true - gravity = Gravity.CENTER - orientation = HORIZONTAL - minimumHeight = height + setFocusable(true) + isClickable = true + gravity = Gravity.CENTER + orientation = HORIZONTAL + minimumHeight = height - setBackgroundResource(R.drawable.cell_button_background) - setPadding(leftPadding, 0, rightPadding, 0) + setBackgroundResource(R.drawable.cell_button_background) + setPadding(leftPadding, 0, rightPadding, 0) - addView(label) + addView(label) - setOnClickListener { onClickListener?.invoke() } - } + setOnClickListener { onClickListener?.invoke() } + } } var onClickListener: (() -> Unit)? = null @@ -101,9 +113,11 @@ open class Cell : LinearLayout { private fun setUp() { if (footer != null) { - cell = LinearLayout(context).apply { - layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - } + cell = + LinearLayout(context).apply { + layoutParams = + LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + } isClickable = false orientation = VERTICAL diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt index 8e44d72733..8c2312044a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CellSwitch.kt @@ -21,13 +21,14 @@ class CellSwitch : LinearLayout { OFF } - var state by observable(State.OFF) { _, oldState, newState -> - animateToState() + var state by + observable(State.OFF) { _, oldState, newState -> + animateToState() - if (oldState != newState) { - listener?.invoke(newState) + if (oldState != newState) { + listener?.invoke(newState) + } } - } var listener: ((State) -> Unit)? = null @@ -35,19 +36,18 @@ class CellSwitch : LinearLayout { private val offColor = context.getColor(R.color.red) private val knobSize = resources.getDimensionPixelSize(R.dimen.cell_switch_knob_size) - private val knobImage = ShapeDrawable(OvalShape()).apply { - paint.apply { - color = offColor - style = Style.FILL - } + private val knobImage = + ShapeDrawable(OvalShape()).apply { + paint.apply { + color = offColor + style = Style.FILL + } - intrinsicWidth = knobSize - intrinsicHeight = knobSize - } + intrinsicWidth = knobSize + intrinsicHeight = knobSize + } - private val knobView = ImageView(context).apply { - setImageDrawable(knobImage) - } + private val knobView = ImageView(context).apply { setImageDrawable(knobImage) } private val knobAnimationDuration = 200L private val knobMaxTranslation = @@ -58,98 +58,101 @@ class CellSwitch : LinearLayout { private var animationIsReversed = false - private val positionAnimation = ValueAnimator.ofFloat(0f, knobMaxTranslation).apply { - addUpdateListener { animation -> - knobView.translationX = animation.animatedValue as Float + private val positionAnimation = + ValueAnimator.ofFloat(0f, knobMaxTranslation).apply { + addUpdateListener { animation -> + knobView.translationX = animation.animatedValue as Float + } + + duration = knobAnimationDuration } - duration = knobAnimationDuration - } + private val colorAnimation = + ValueAnimator.ofArgb(offColor, onColor).apply { + addUpdateListener { animation -> + knobImage.paint.color = animation.animatedValue as Int + knobImage.invalidateSelf() + } - private val colorAnimation = ValueAnimator.ofArgb(offColor, onColor).apply { - addUpdateListener { animation -> - knobImage.paint.color = animation.animatedValue as Int - knobImage.invalidateSelf() + duration = knobAnimationDuration } - duration = knobAnimationDuration - } + private val gestureListener = + object : OnGestureListener { + private var isScrolling: Boolean = false + private var scrollPosition: Float = 0f - private val gestureListener = object : OnGestureListener { - private var isScrolling: Boolean = false - private var scrollPosition: Float = 0f + override fun onDown(event: MotionEvent): Boolean { + scrollPosition = knobView.translationX + return true + } - override fun onDown(event: MotionEvent): Boolean { - scrollPosition = knobView.translationX - return true - } + override fun onFling( + downEvent: MotionEvent, + upEvent: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + if (velocityX > 0f) { + state = State.ON + } else if (velocityX < 0f) { + state = State.OFF + } - override fun onFling( - downEvent: MotionEvent, - upEvent: MotionEvent, - velocityX: Float, - velocityY: Float - ): Boolean { - if (velocityX > 0f) { - state = State.ON - } else if (velocityX < 0f) { - state = State.OFF + return true } - return true - } + override fun onLongPress(event: MotionEvent) {} - override fun onLongPress(event: MotionEvent) {} + override fun onScroll( + downEvent: MotionEvent, + moveEvent: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + isScrolling = true + scrollPosition -= distanceX - override fun onScroll( - downEvent: MotionEvent, - moveEvent: MotionEvent, - distanceX: Float, - distanceY: Float - ): Boolean { - isScrolling = true - scrollPosition -= distanceX + var fraction = scrollPosition / knobMaxTranslation + val playTime = (fraction * knobAnimationDuration).toLong() - var fraction = scrollPosition / knobMaxTranslation - val playTime = (fraction * knobAnimationDuration).toLong() + colorAnimation.pause() + positionAnimation.pause() - colorAnimation.pause() - positionAnimation.pause() + colorAnimation.currentPlayTime = playTime + positionAnimation.currentPlayTime = playTime - colorAnimation.currentPlayTime = playTime - positionAnimation.currentPlayTime = playTime + return true + } - return true - } + override fun onShowPress(event: MotionEvent) {} - override fun onShowPress(event: MotionEvent) {} + override fun onSingleTapUp(event: MotionEvent): Boolean { + when (state) { + State.ON -> state = State.OFF + State.OFF -> state = State.ON + } - override fun onSingleTapUp(event: MotionEvent): Boolean { - when (state) { - State.ON -> state = State.OFF - State.OFF -> state = State.ON + return true } - return true - } - - fun onUp(): Boolean { - if (!isScrolling) { - return false - } + fun onUp(): Boolean { + if (!isScrolling) { + return false + } - if (knobPosition <= 0.5f) { - state = State.OFF - } else { - state = State.ON - } + if (knobPosition <= 0.5f) { + state = State.OFF + } else { + state = State.ON + } - isScrolling = false - scrollPosition = 0f + isScrolling = false + scrollPosition = 0f - return true + return true + } } - } private val gestureDetector = GestureDetector(context, gestureListener) @@ -157,8 +160,11 @@ class CellSwitch : LinearLayout { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { setBackground(resources.getDrawable(R.drawable.cell_switch_background, null)) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CopyableInformationView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CopyableInformationView.kt index 9e5d68d585..dabc1fb218 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CopyableInformationView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CopyableInformationView.kt @@ -20,8 +20,11 @@ class CopyableInformationView : InformationView { loadAttributes(attributes) } - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) { + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) { loadAttributes(attributes) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CustomRecyclerView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CustomRecyclerView.kt index 374bb3a240..902f6c6acb 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CustomRecyclerView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/CustomRecyclerView.kt @@ -17,15 +17,19 @@ class CustomRecyclerView : RecyclerView, ListenableScrollableView { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { - itemAnimator = customItemAnimator.apply { - onMove = { horizontalDelta, verticalDelta -> - dispatchScrollEvent(horizontalDelta, verticalDelta) + itemAnimator = + customItemAnimator.apply { + onMove = { horizontalDelta, verticalDelta -> + dispatchScrollEvent(horizontalDelta, verticalDelta) + } } - } } override fun setLayoutManager(layoutManager: LayoutManager?) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/HeaderBar.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/HeaderBar.kt index e1688ace21..6f7ca5e8c5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/HeaderBar.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/HeaderBar.kt @@ -14,7 +14,9 @@ import net.mullvad.mullvadvpn.ui.MainActivity import net.mullvad.mullvadvpn.ui.StatusBarPainter import net.mullvad.mullvadvpn.ui.paintStatusBar -class HeaderBar @JvmOverloads constructor( +class HeaderBar +@JvmOverloads +constructor( context: Context, attributes: AttributeSet? = null, defStyleAttr: Int = 0, @@ -27,18 +29,20 @@ class HeaderBar @JvmOverloads constructor( private val securedColor = ContextCompat.getColor(context, R.color.green) private val unsecuredColor = ContextCompat.getColor(context, R.color.red) - var tunnelState by observable<TunnelState?>(null) { _, _, state -> - val backgroundColor = if (state == null) { - disabledColor - } else if (state.isSecured()) { - securedColor - } else { - unsecuredColor - } + var tunnelState by + observable<TunnelState?>(null) { _, _, state -> + val backgroundColor = + if (state == null) { + disabledColor + } else if (state.isSecured()) { + securedColor + } else { + unsecuredColor + } - container.setBackgroundColor(backgroundColor) - paintStatusBar(backgroundColor) - } + container.setBackgroundColor(backgroundColor) + paintStatusBar(backgroundColor) + } init { gravity = Gravity.CENTER_VERTICAL @@ -46,9 +50,7 @@ class HeaderBar @JvmOverloads constructor( settingsButton.apply { isEnabled = true - setOnClickListener { - (context as? MainActivity)?.openSettings() - } + setOnClickListener { (context as? MainActivity)?.openSettings() } } tunnelState = null diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/InformationView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/InformationView.kt index 42f78b3b4a..b7547a8761 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/InformationView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/InformationView.kt @@ -49,18 +49,18 @@ open class InformationView : LinearLayout { var information by observable<String?>(null) { _, _, _ -> updateStatus() } var errorColor by observable(context.getColor(R.color.red)) { _, _, _ -> updateStatus() } - var informationColor by observable(context.getColor(R.color.white)) { _, _, _ -> - updateStatus() - } + var informationColor by + observable(context.getColor(R.color.white)) { _, _, _ -> updateStatus() } var maxLength by observable(0) { _, _, _ -> updateStatus() } var whenMissing by observable(WhenMissing.Nothing) { _, _, _ -> updateStatus() } var shouldEnable by observable(false) { _, _, _ -> updateEnabled() } - var onClick by observable<(() -> Unit)?>(null) { _, _, callback -> - container.setFocusable(callback != null) - } + var onClick by + observable<(() -> Unit)?>(null) { _, _, callback -> + container.setFocusable(callback != null) + } sealed class Masking { object None : Masking() @@ -68,40 +68,40 @@ open class InformationView : LinearLayout { data class Show(val transformationMethod: TransformationMethod) : Masking() } - var informationState by observable<Masking>(Masking.None) { _, _, newState -> - when (newState) { - is Masking.Hide -> { - informationDisplay.transformationMethod = newState.transformationMethod + var informationState by + observable<Masking>(Masking.None) { _, _, newState -> + when (newState) { + is Masking.Hide -> { + informationDisplay.transformationMethod = newState.transformationMethod - toggleMaskingButton.apply { - visibility = VISIBLE - contentDescription = context.getString(R.string.show_account_number) - background = AppCompatResources.getDrawable(context, R.drawable.icon_show) + toggleMaskingButton.apply { + visibility = VISIBLE + contentDescription = context.getString(R.string.show_account_number) + background = AppCompatResources.getDrawable(context, R.drawable.icon_show) + } } - } + is Masking.Show -> { + informationDisplay.transformationMethod = newState.transformationMethod - is Masking.Show -> { - informationDisplay.transformationMethod = newState.transformationMethod - - toggleMaskingButton.apply { - visibility = VISIBLE - contentDescription = context.getString(R.string.hide_account_number) - background = AppCompatResources.getDrawable(context, R.drawable.icon_hide) + toggleMaskingButton.apply { + visibility = VISIBLE + contentDescription = context.getString(R.string.hide_account_number) + background = AppCompatResources.getDrawable(context, R.drawable.icon_hide) + } + } + is Masking.None -> { + informationDisplay.transformationMethod = null + toggleMaskingButton.visibility = INVISIBLE } } - is Masking.None -> { - informationDisplay.transformationMethod = null - toggleMaskingButton.visibility = INVISIBLE - } + updateStatus() } - updateStatus() - } - - var onToggleMaskingClicked by observable<(() -> Unit)?>(null) { _, _, callback -> - toggleMaskingButton.setOnClickListener { callback?.invoke() } - } + var onToggleMaskingClicked by + observable<(() -> Unit)?>(null) { _, _, callback -> + toggleMaskingButton.setOnClickListener { callback?.invoke() } + } constructor(context: Context) : super(context) {} @@ -109,8 +109,11 @@ open class InformationView : LinearLayout { loadAttributes(attributes) } - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) { + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) { loadAttributes(attributes) } @@ -146,14 +149,11 @@ open class InformationView : LinearLayout { errorColor = getInteger(R.styleable.InformationView_errorColor, errorColor) maxLength = getInteger(R.styleable.InformationView_maxLength, 0) - informationColor = getInteger( - R.styleable.InformationView_informationColor, - informationColor - ) + informationColor = + getInteger(R.styleable.InformationView_informationColor, informationColor) - whenMissing = WhenMissing.fromCode( - getInteger(R.styleable.InformationView_whenMissing, 0) - ) + whenMissing = + WhenMissing.fromCode(getInteger(R.styleable.InformationView_whenMissing, 0)) } finally { recycle() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ListenableScrollView.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ListenableScrollView.kt index 6f2ae01b8b..683341686f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ListenableScrollView.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ListenableScrollView.kt @@ -17,8 +17,11 @@ class ListenableScrollView : ScrollView, ListenableScrollableView { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) override fun onScrollChanged(left: Int, top: Int, oldLeft: Int, oldTop: Int) { super.onScrollChanged(left, top, oldLeft, oldTop) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt index a334618e3c..ee1901774a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NavigateCell.kt @@ -9,15 +9,16 @@ import kotlin.reflect.KClass import net.mullvad.mullvadvpn.R open class NavigateCell : Cell { - private val chevron = ImageView(context).apply { - val width = resources.getDimensionPixelSize(R.dimen.chevron_width) - val height = resources.getDimensionPixelSize(R.dimen.chevron_height) + private val chevron = + ImageView(context).apply { + val width = resources.getDimensionPixelSize(R.dimen.chevron_width) + val height = resources.getDimensionPixelSize(R.dimen.chevron_height) - layoutParams = LayoutParams(width, height, 0.0f) - alpha = 0.6f + layoutParams = LayoutParams(width, height, 0.0f) + alpha = 0.6f - setImageResource(R.drawable.icon_chevron) - } + setImageResource(R.drawable.icon_chevron) + } var targetFragment: KClass<out Fragment>? = null @@ -25,8 +26,11 @@ open class NavigateCell : Cell { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { cell.addView(chevron) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt index db1c8cf43c..86073f2079 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/NotificationBanner.kt @@ -22,37 +22,39 @@ import net.mullvad.mullvadvpn.util.JobTracker class NotificationBanner : FrameLayout { private val jobTracker = JobTracker() - private val animationListener = object : AnimatorListener { - override fun onAnimationCancel(animation: Animator) {} - override fun onAnimationRepeat(animation: Animator) {} + private val animationListener = + object : AnimatorListener { + override fun onAnimationCancel(animation: Animator) {} + override fun onAnimationRepeat(animation: Animator) {} - override fun onAnimationStart(animation: Animator) { - visibility = View.VISIBLE - } + override fun onAnimationStart(animation: Animator) { + visibility = View.VISIBLE + } - override fun onAnimationEnd(animation: Animator) { - synchronized(this@NotificationBanner) { - if (reversedAnimation) { - // Banner is now hidden - val notification = notifications.current + override fun onAnimationEnd(animation: Animator) { + synchronized(this@NotificationBanner) { + if (reversedAnimation) { + // Banner is now hidden + val notification = notifications.current - visibility = View.INVISIBLE + visibility = View.INVISIBLE - if (notification != null) { - // Notification changed, restart animation - update(notification) - reversedAnimation = false - animation.start() + if (notification != null) { + // Notification changed, restart animation + update(notification) + reversedAnimation = false + animation.start() + } } } } } - } - private val animation = ObjectAnimator.ofFloat(this, "translationY", 0.0f).apply { - addListener(animationListener) - setDuration(350) - } + private val animation = + ObjectAnimator.ofFloat(this, "translationY", 0.0f).apply { + addListener(animationListener) + setDuration(350) + } private val container = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE).let { service -> @@ -72,23 +74,22 @@ class NotificationBanner : FrameLayout { private var reversedAnimation = false val notifications = InAppNotificationController { _ -> - synchronized(this@NotificationBanner) { - animateChange() - } + synchronized(this@NotificationBanner) { animateChange() } } constructor(context: Context) : super(context) constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { setBackgroundResource(R.color.darkBlue) - setOnClickListener { - jobTracker.newUiJob("click") { onClick() } - } + setOnClickListener { jobTracker.newUiJob("click") { onClick() } } visibility = View.INVISIBLE } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/RedeemVoucherButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/RedeemVoucherButton.kt index c823a3f3b9..885fceef2a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/RedeemVoucherButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/RedeemVoucherButton.kt @@ -11,8 +11,11 @@ class RedeemVoucherButton : Button { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) fun prepare( fragmentManager: FragmentManager?, diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SitePaymentButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SitePaymentButton.kt index 35d0c2326f..9fbe71337e 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SitePaymentButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SitePaymentButton.kt @@ -10,14 +10,18 @@ class SitePaymentButton : UrlButton { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) - var newAccount by observable(false) { _, _, isNewAccount -> - if (isNewAccount) { - label = context.getString(R.string.buy_credit) - } else { - label = context.getString(R.string.buy_more_credit) + var newAccount by + observable(false) { _, _, isNewAccount -> + if (isNewAccount) { + label = context.getString(R.string.buy_credit) + } else { + label = context.getString(R.string.buy_more_credit) + } } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SwitchLocationButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SwitchLocationButton.kt index a6a8e7dbfb..02ec153f0a 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SwitchLocationButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/SwitchLocationButton.kt @@ -20,9 +20,10 @@ class SwitchLocationButton : FrameLayout { inflater.inflate(R.layout.switch_location_button, this) } - private val buttonWithLabel = container.findViewById<View>(R.id.button_with_label).apply { - setOnClickListener { onClick?.invoke() } - } + private val buttonWithLabel = + container.findViewById<View>(R.id.button_with_label).apply { + setOnClickListener { onClick?.invoke() } + } private val buttonWithLocation = container.findViewById<TextView>(R.id.button_with_location).apply { @@ -31,32 +32,37 @@ class SwitchLocationButton : FrameLayout { var onClick: (() -> Unit)? = null - var location by observable<RelayItem?>(null) { _, _, location -> - buttonWithLocation.text = location?.locationName ?: "" - } + var location by + observable<RelayItem?>(null) { _, _, location -> + buttonWithLocation.text = location?.locationName ?: "" + } - var tunnelState by observable<TunnelState>(TunnelState.Disconnected) { _, _, state -> - when (state) { - is TunnelState.Disconnected -> showLocation() - is TunnelState.Disconnecting -> { - when (state.actionAfterDisconnect) { - ActionAfterDisconnect.Nothing -> showLocation() - ActionAfterDisconnect.Block -> showLocation() - ActionAfterDisconnect.Reconnect -> showLabel() + var tunnelState by + observable<TunnelState>(TunnelState.Disconnected) { _, _, state -> + when (state) { + is TunnelState.Disconnected -> showLocation() + is TunnelState.Disconnecting -> { + when (state.actionAfterDisconnect) { + ActionAfterDisconnect.Nothing -> showLocation() + ActionAfterDisconnect.Block -> showLocation() + ActionAfterDisconnect.Reconnect -> showLabel() + } } + is TunnelState.Connecting -> showLabel() + is TunnelState.Connected -> showLabel() + is TunnelState.Error -> showLocation() } - is TunnelState.Connecting -> showLabel() - is TunnelState.Connected -> showLabel() - is TunnelState.Error -> showLocation() } - } constructor(context: Context) : super(context) constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) private fun showLabel() { updateButton(buttonWithLabel, true) @@ -72,11 +78,12 @@ class SwitchLocationButton : FrameLayout { button.apply { setEnabled(show) - visibility = if (show) { - View.VISIBLE - } else { - View.INVISIBLE - } + visibility = + if (show) { + View.VISIBLE + } else { + View.INVISIBLE + } } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt index f9ae44fd56..f47347539f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/ToggleCell.kt @@ -4,9 +4,10 @@ import android.content.Context import android.util.AttributeSet class ToggleCell : Cell { - private val toggle = CellSwitch(context).apply { - layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) - } + private val toggle = + CellSwitch(context).apply { + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) + } var state get() = toggle.state @@ -24,8 +25,11 @@ class ToggleCell : Cell { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { onClickListener = { toggle() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt index c1b700e433..db3da99270 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlButton.kt @@ -9,8 +9,11 @@ open class UrlButton : Button { constructor(context: Context, attributes: AttributeSet) : super(context, attributes) - constructor(context: Context, attributes: AttributeSet, defaultStyleAttribute: Int) : - super(context, attributes, defaultStyleAttribute) + constructor( + context: Context, + attributes: AttributeSet, + defaultStyleAttribute: Int + ) : super(context, attributes, defaultStyleAttribute) init { super.setEnabled(false) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlCell.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlCell.kt index 50727f74a6..1d263901b5 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlCell.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/widget/UrlCell.kt @@ -8,12 +8,13 @@ import android.widget.ImageView import net.mullvad.mullvadvpn.R open class UrlCell : Cell { - private val externalLinkIcon = ImageView(context).apply { - layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) - alpha = 0.6f + private val externalLinkIcon = + ImageView(context).apply { + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0.0f) + alpha = 0.6f - setImageResource(R.drawable.icon_extlink) - } + setImageResource(R.drawable.icon_extlink) + } var url: Uri? = null @@ -34,9 +35,7 @@ open class UrlCell : Cell { private fun loadAttributes(attributes: AttributeSet?) { context.theme.obtainStyledAttributes(attributes, R.styleable.Url, 0, 0).apply { try { - getString(R.styleable.Url_url)?.let { urlString -> - url = Uri.parse(urlString) - } + getString(R.styleable.Url_url)?.let { urlString -> url = Uri.parse(urlString) } } finally { recycle() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/AdapterWithHeader.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/AdapterWithHeader.kt index a239a4d9c8..89b0057f63 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/AdapterWithHeader.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/AdapterWithHeader.kt @@ -11,59 +11,63 @@ class AdapterWithHeader<H : ViewHolder>( val adapter: RecyclerView.Adapter<H>, val headerLayoutId: Int ) : RecyclerView.Adapter<HeaderOrHolder<H>>() { - private val observer = object : RecyclerView.AdapterDataObserver() { - override fun onChanged() { - notifyDataSetChanged() - } + private val observer = + object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + notifyDataSetChanged() + } - override fun onItemRangeChanged(start: Int, count: Int) { - notifyItemRangeChanged(start + 1, count) - } + override fun onItemRangeChanged(start: Int, count: Int) { + notifyItemRangeChanged(start + 1, count) + } - override fun onItemRangeChanged(start: Int, count: Int, payload: Any?) { - notifyItemRangeChanged(start + 1, count, payload) - } + override fun onItemRangeChanged(start: Int, count: Int, payload: Any?) { + notifyItemRangeChanged(start + 1, count, payload) + } - override fun onItemRangeInserted(start: Int, count: Int) { - notifyItemRangeInserted(start + 1, count) - } + override fun onItemRangeInserted(start: Int, count: Int) { + notifyItemRangeInserted(start + 1, count) + } + + override fun onItemRangeMoved(from: Int, to: Int, count: Int) { + if (from == to) { + notifyItemRangeChanged(from + 1, count) + } else { + val sourceStart = from + 1 + val sourceEnd = sourceStart + count + val destinationStart = to + 1 + val destinationEnd = destinationStart + count - override fun onItemRangeMoved(from: Int, to: Int, count: Int) { - if (from == to) { - notifyItemRangeChanged(from + 1, count) - } else { - val sourceStart = from + 1 - val sourceEnd = sourceStart + count - val destinationStart = to + 1 - val destinationEnd = destinationStart + count + val ascendingIndices = + (sourceStart..sourceEnd).zip(destinationStart..destinationEnd) - val ascendingIndices = - (sourceStart..sourceEnd).zip(destinationStart..destinationEnd) + val indices = + if (from < to) { + ascendingIndices.asReversed() + } else { + ascendingIndices + } - val indices = if (from < to) { - ascendingIndices.asReversed() - } else { - ascendingIndices + for ((source, destination) in indices) { + notifyItemMoved(source, destination) + } } + } - for ((source, destination) in indices) { - notifyItemMoved(source, destination) - } + override fun onItemRangeRemoved(start: Int, count: Int) { + notifyItemRangeRemoved(start + 1, count) } } - override fun onItemRangeRemoved(start: Int, count: Int) { - notifyItemRangeRemoved(start + 1, count) + private var headerView: View? by + observable<View?>(null) { _, _, newView -> + newView?.let { view -> onHeaderAvailable?.invoke(view) } } - } - private var headerView: View? by observable<View?>(null) { _, _, newView -> - newView?.let { view -> onHeaderAvailable?.invoke(view) } - } - - var onHeaderAvailable by observable<((View) -> Unit)?>(null) { _, _, listener -> - headerView?.let { header -> listener?.invoke(header) } - } + var onHeaderAvailable by + observable<((View) -> Unit)?>(null) { _, _, listener -> + headerView?.let { header -> listener?.invoke(header) } + } init { adapter.registerAdapterDataObserver(observer) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/CacheExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/CacheExtensions.kt index 62172231f9..ae79c1364f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/CacheExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/CacheExtensions.kt @@ -16,7 +16,5 @@ fun AppVersionInfoCache.appVersionCallbackFlow() = callbackFlow { ) ) } - awaitClose { - onUpdate = null - } + awaitClose { onUpdate = null } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ChangeMonitor.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ChangeMonitor.kt index 398177db99..c8361a36c4 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ChangeMonitor.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ChangeMonitor.kt @@ -6,11 +6,12 @@ class ChangeMonitor { var changed = false private set - fun <T> monitor(initialValue: T) = observable(initialValue) { _, oldValue, newValue -> - if (oldValue != newValue) { - changed = true + fun <T> monitor(initialValue: T) = + observable(initialValue) { _, oldValue, newValue -> + if (oldValue != newValue) { + changed = true + } } - } fun reset() { changed = false diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Debouncer.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Debouncer.kt index 7afb4c508f..677d981417 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Debouncer.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Debouncer.kt @@ -23,17 +23,18 @@ class Debouncer<T>(initialValue: T, val intervalInMs: Long = 0) { var debouncedValue = initialValue private set - var rawValue by observable(initialValue) { _, oldValue, newValue -> - if (newValue != oldValue) { - jobTracker.cancelJob("notifyNewValue") + var rawValue by + observable(initialValue) { _, oldValue, newValue -> + if (newValue != oldValue) { + jobTracker.cancelJob("notifyNewValue") - if (newValue != debouncedValue) { - jobTracker.newUiJob("notifyNewValue") { - delay(intervalInMs) - listener?.invoke(newValue) - debouncedValue = newValue + if (newValue != debouncedValue) { + jobTracker.newUiJob("notifyNewValue") { + delay(intervalInMs) + listener?.invoke(newValue) + debouncedValue = newValue + } } } } - } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DeviceStateExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DeviceStateExtensions.kt index d41a628bec..13b8f84599 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DeviceStateExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DeviceStateExtensions.kt @@ -6,16 +6,12 @@ import net.mullvad.mullvadvpn.model.DeviceState const val UNKNOWN_STATE_DEBOUNCE_DELAY_MILLISECONDS = 2000L private const val ZERO_DEBOUNCE_DELAY_MILLISECONDS = 0L -fun DeviceState.addDebounceForUnknownState( - delay: Long -): Long { +fun DeviceState.addDebounceForUnknownState(delay: Long): Long { return addDebounceForStates(delay, DeviceState.Unknown::class) } -fun <T> DeviceState.addDebounceForStates( - delay: Long, - vararg states: KClass<T> -): Long where T : DeviceState { +fun <T> DeviceState.addDebounceForStates(delay: Long, vararg states: KClass<T>): Long where +T : DeviceState { val result = states.any { this::class == it } return if (result) { delay diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DispatchingFlow.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DispatchingFlow.kt index af66a092ba..fc46783ab0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DispatchingFlow.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/DispatchingFlow.kt @@ -14,10 +14,7 @@ import kotlinx.coroutines.flow.consumeAsFlow class DispatchingFlow<T : Any>(private val upstream: Flow<T>) : Flow<T> { private val subscribers = ConcurrentHashMap<KClass<out T>, SendChannel<T>>() - fun <V : T> subscribe( - variant: KClass<V>, - capacity: Int = Channel.CONFLATED - ): Flow<V> { + fun <V : T> subscribe(variant: KClass<V>, capacity: Int = Channel.CONFLATED): Flow<V> { val channel = Channel<V>(capacity) // This is safe because `collect` will only send to this channel if the instance class is V diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ErrorStateExtension.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ErrorStateExtension.kt index 2e9c96a169..ec4091b55d 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ErrorStateExtension.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/ErrorStateExtension.kt @@ -19,14 +19,9 @@ fun ErrorState.getErrorNotificationResources(context: Context): ErrorNotificatio cause is ErrorStateCause.VpnPermissionDenied -> { resolveAlwaysOnVpnErrorNotificationMessage(context.getAlwaysOnVpnAppName()) } - isBlocking -> ErrorNotificationMessage( - R.string.blocking_all_connections, - cause.errorMessageId() - ) - else -> ErrorNotificationMessage( - R.string.critical_error, - R.string.failed_to_block_internet - ) + isBlocking -> + ErrorNotificationMessage(R.string.blocking_all_connections, cause.errorMessageId()) + else -> ErrorNotificationMessage(R.string.critical_error, R.string.failed_to_block_internet) } } 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 4d715a9aec..d7cc4631d0 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 @@ -23,34 +23,36 @@ fun <T> SendChannel<T>.safeOffer(element: T): Boolean { return runCatching { trySend(element).isSuccess }.getOrDefault(false) } -fun Animation.transitionFinished(): Flow<Unit> = callbackFlow<Unit> { - val transitionAnimationListener = object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation?) {} - override fun onAnimationEnd(animation: Animation?) { - safeOffer(Unit) - } +fun Animation.transitionFinished(): Flow<Unit> = + callbackFlow<Unit> { + val transitionAnimationListener = + object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation?) {} + override fun onAnimationEnd(animation: Animation?) { + safeOffer(Unit) + } - override fun onAnimationRepeat(animation: Animation?) {} - } - setAnimationListener(transitionAnimationListener) - awaitClose { - Dispatchers.Main.dispatch(EmptyCoroutineContext) { - setAnimationListener(null) + override fun onAnimationRepeat(animation: Animation?) {} + } + setAnimationListener(transitionAnimationListener) + awaitClose { + Dispatchers.Main.dispatch(EmptyCoroutineContext) { setAnimationListener(null) } + } } - } -}.take(1) + .take(1) fun Context.bindServiceFlow(intent: Intent, flags: Int = 0): Flow<ServiceResult> = callbackFlow { - val connectionCallback = object : ServiceConnection { - override fun onServiceConnected(className: ComponentName, binder: IBinder) { - safeOffer(ServiceResult(binder)) - } + val connectionCallback = + object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, binder: IBinder) { + safeOffer(ServiceResult(binder)) + } - override fun onServiceDisconnected(className: ComponentName) { - safeOffer(ServiceResult.NOT_CONNECTED) - bindService(intent, this, flags) + override fun onServiceDisconnected(className: ComponentName) { + safeOffer(ServiceResult.NOT_CONNECTED) + bindService(intent, this, flags) + } } - } bindService(intent, connectionCallback, flags) @@ -80,8 +82,9 @@ fun <R> Flow<ServiceConnectionState>.flatMapReadyConnectionOrDefault( } } -fun <T> callbackFlowFromNotifier(notifier: EventNotifier<T>) = callbackFlow<T> { - val handler: (T) -> Unit = { value -> trySend(value) } - notifier.subscribe(this, handler) - awaitClose { notifier.unsubscribe(this) } -} +fun <T> callbackFlowFromNotifier(notifier: EventNotifier<T>) = + callbackFlow<T> { + val handler: (T) -> Unit = { value -> trySend(value) } + notifier.subscribe(this, handler) + awaitClose { notifier.unsubscribe(this) } + } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Intermittent.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Intermittent.kt index 1fe3a60fcd..dc8ee67cf8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Intermittent.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/Intermittent.kt @@ -67,19 +67,18 @@ class Intermittent<T> { synchronized(this@Intermittent) { val previousUpdate = updateJob - updateJob = GlobalScope.launch(Dispatchers.Default) { - previousUpdate?.join() - update(newValue) - } + updateJob = + GlobalScope.launch(Dispatchers.Default) { + previousUpdate?.join() + update(newValue) + } } } // Helper method that provides a simple way to change the wrapped value. // The method returns a property delegate that will spawn a coroutine to update the wrapped // value every time the property is written to. - fun source() = observable<T?>(null) { _, _, newValue -> - spawnUpdate(newValue) - } + fun source() = observable<T?>(null) { _, _, newValue -> spawnUpdate(newValue) } fun onDestroy() { notifier.unsubscribeAll() diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/JobTracker.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/JobTracker.kt index 0b950d9a55..fa027af4b1 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/JobTracker.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/JobTracker.kt @@ -26,9 +26,7 @@ class JobTracker { GlobalScope.launch(Dispatchers.Default) { job.join() - synchronized(jobs) { - jobs.remove(jobId) - } + synchronized(jobs) { jobs.remove(jobId) } } ) @@ -65,11 +63,7 @@ class JobTracker { } fun cancelJob(name: String) { - synchronized(namedJobs) { - namedJobs.remove(name)?.let { oldJobId -> - cancelJob(oldJobId) - } - } + synchronized(namedJobs) { namedJobs.remove(name)?.let { oldJobId -> cancelJob(oldJobId) } } } fun cancelJob(jobId: Long) { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt index 67491ad6fe..cb631d8af6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SdkUtils.kt @@ -40,7 +40,6 @@ object SdkUtils { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { getInstalledPackages(PackageManager.PackageInfoFlags.of(flags.toLong())) } else { - @Suppress("DEPRECATION") - getInstalledPackages(flags) + @Suppress("DEPRECATION") getInstalledPackages(flags) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt index 9154f102ef..4dd6b7bc1b 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedInputFormatter.kt @@ -72,19 +72,16 @@ class SegmentedInputFormatter(val input: EditText, var separator: Char) : TextWa } private fun isValidInput(string: String): Boolean { - return string - .asSequence() - .withIndex() - .all { item -> - val index = item.index - val character = item.value + return string.asSequence().withIndex().all { item -> + val index = item.index + val character = item.value - if ((index + 1) % separatorSkipCount == 0) { - character == separator - } else { - isValidInputCharacter(character) - } + if ((index + 1) % separatorSkipCount == 0) { + character == separator + } else { + isValidInputCharacter(character) } + } } private fun formatInput(input: Editable) { @@ -97,8 +94,9 @@ class SegmentedInputFormatter(val input: EditText, var separator: Char) : TextWa val segmentEnd = index + segmentSize - 1 val separatorPosition = segmentEnd + 1 - changed = formatSegment(input, segmentStart..segmentEnd) || - formatSeparator(input, separatorPosition) + changed = + formatSegment(input, segmentStart..segmentEnd) || + formatSeparator(input, separatorPosition) index = separatorPosition + 1 } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedTextFormatter.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedTextFormatter.kt index 8e08f3c742..4ba6fe97e0 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedTextFormatter.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SegmentedTextFormatter.kt @@ -4,10 +4,11 @@ class SegmentedTextFormatter(var separator: Char) { var isValidInputCharacter: (Char) -> Boolean = { _ -> true } var segmentSize = 4 - fun format(string: String) = string - .asSequence() - .filter(isValidInputCharacter) - .chunked(segmentSize) - .map { segmentCharacters -> segmentCharacters.joinToString("") } - .joinToString("$separator") + fun format(string: String) = + string + .asSequence() + .filter(isValidInputCharacter) + .chunked(segmentSize) + .map { segmentCharacters -> segmentCharacters.joinToString("") } + .joinToString("$separator") } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SmartDeferred.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SmartDeferred.kt index 61050bc894..4035f5b182 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SmartDeferred.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/SmartDeferred.kt @@ -13,9 +13,7 @@ class SmartDeferred<T>(private val deferred: Deferred<T>) { fun awaitThen(action: T.() -> Unit): Long? { if (active) { return jobTracker.newJob( - GlobalScope.launch(Dispatchers.Default) { - deferred.await().action() - } + GlobalScope.launch(Dispatchers.Default) { deferred.await().action() } ) } else { return null diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/StringExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/StringExtensions.kt index f2daffccaf..c0139d9d2c 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/StringExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/StringExtensions.kt @@ -7,9 +7,7 @@ private const val EXPIRY_FORMAT = "YYYY-MM-dd HH:mm:ss z" fun String.capitalizeFirstCharOfEachWord(): String { return split(" ") - .joinToString(" ") { word -> - word.replaceFirstChar { firstChar -> firstChar.uppercase() } - } + .joinToString(" ") { word -> word.replaceFirstChar { firstChar -> firstChar.uppercase() } } .trimEnd() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeAgoFormatter.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeAgoFormatter.kt index 1136a21814..3ae5f499cd 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeAgoFormatter.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/util/TimeAgoFormatter.kt @@ -7,9 +7,7 @@ import org.joda.time.Duration import org.joda.time.PeriodType class TimeAgoFormatter(val resources: Resources) { - private val periodType = PeriodType.standard() - .withMillisRemoved() - .withSecondsRemoved() + private val periodType = PeriodType.standard().withMillisRemoved().withSecondsRemoved() fun format(instant: DateTime): String { val elapsedTime = Duration(instant, DateTime.now()) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt index 7d456fb680..c29573e819 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModel.kt @@ -29,30 +29,30 @@ class AdvancedSettingsViewModel( private val dialogState = MutableStateFlow<AdvancedSettingsDialogState>(AdvancedSettingsDialogState.NoDialog) - private val vmState = combine( - repository.settingsUpdates, - dialogState - ) { settings, interaction -> - AdvancedSettingsViewModelState( - mtuValue = settings?.mtuString() ?: "", - isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, - customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), - isAllowLanEnabled = settings?.allowLan ?: false, - dialogState = interaction - ) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(), - AdvancedSettingsViewModelState.default() - ) + private val vmState = + combine(repository.settingsUpdates, dialogState) { settings, interaction -> + AdvancedSettingsViewModelState( + mtuValue = settings?.mtuString() ?: "", + isCustomDnsEnabled = settings?.isCustomDnsEnabled() ?: false, + customDnsList = settings?.addresses()?.asStringAddressList() ?: listOf(), + isAllowLanEnabled = settings?.allowLan ?: false, + dialogState = interaction + ) + } + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(), + AdvancedSettingsViewModelState.default() + ) - val uiState = vmState - .map(AdvancedSettingsViewModelState::toUiState) - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(), - AdvancedSettingsUiState.DefaultUiState() - ) + val uiState = + vmState + .map(AdvancedSettingsViewModelState::toUiState) + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(), + AdvancedSettingsUiState.DefaultUiState() + ) fun onMtuCellClick() { dialogState.update { AdvancedSettingsDialogState.MtuDialog(vmState.value.mtuValue) } @@ -62,34 +62,36 @@ class AdvancedSettingsViewModel( dialogState.update { AdvancedSettingsDialogState.MtuDialog(value) } } - fun onSaveMtuClick() = viewModelScope.launch(dispatcher) { - val dialog = dialogState.value as? AdvancedSettingsDialogState.MtuDialog - dialog?.mtuEditValue?.toIntOrNull()?.takeIf { it.isValidMtu() }?.let { mtu -> - repository.setWireguardMtu(mtu) + fun onSaveMtuClick() = + viewModelScope.launch(dispatcher) { + val dialog = dialogState.value as? AdvancedSettingsDialogState.MtuDialog + dialog + ?.mtuEditValue + ?.toIntOrNull() + ?.takeIf { it.isValidMtu() } + ?.let { mtu -> repository.setWireguardMtu(mtu) } + hideDialog() } - hideDialog() - } - fun onRestoreMtuClick() = viewModelScope.launch(dispatcher) { - repository.setWireguardMtu(null) - hideDialog() - } + fun onRestoreMtuClick() = + viewModelScope.launch(dispatcher) { + repository.setWireguardMtu(null) + hideDialog() + } fun onCancelDialogClick() { hideDialog() } fun onDnsClick(index: Int? = null) { - val stagedDns = if (index == null) { - StagedDns.NewDns(CustomDnsItem.default()) - } else { - vmState.value.customDnsList.getOrNull(index)?.let { listItem -> - StagedDns.EditDns( - item = listItem, - index = index - ) + val stagedDns = + if (index == null) { + StagedDns.NewDns(CustomDnsItem.default()) + } else { + vmState.value.customDnsList.getOrNull(index)?.let { listItem -> + StagedDns.EditDns(item = listItem, index = index) + } } - } if (stagedDns != null) { dialogState.update { AdvancedSettingsDialogState.DnsDialog(stagedDns) } @@ -100,100 +102,101 @@ class AdvancedSettingsViewModel( dialogState.update { state -> val dialog = state as? AdvancedSettingsDialogState.DnsDialog ?: return - val error = when { - ipAddress.isBlank() || ipAddress.isValidIp().not() -> { - StagedDns.ValidationResult.InvalidAddress - } - ipAddress.isDuplicateDns((state.stagedDns as? StagedDns.EditDns)?.index) -> { - StagedDns.ValidationResult.DuplicateAddress + val error = + when { + ipAddress.isBlank() || ipAddress.isValidIp().not() -> { + StagedDns.ValidationResult.InvalidAddress + } + ipAddress.isDuplicateDns((state.stagedDns as? StagedDns.EditDns)?.index) -> { + StagedDns.ValidationResult.DuplicateAddress + } + else -> StagedDns.ValidationResult.Success } - else -> StagedDns.ValidationResult.Success - } return@update AdvancedSettingsDialogState.DnsDialog( - stagedDns = if (dialog.stagedDns is StagedDns.EditDns) { - StagedDns.EditDns( - item = CustomDnsItem( - address = ipAddress, - isLocal = ipAddress.isLocalAddress() - ), - validationResult = error, - index = dialog.stagedDns.index - ) - } else { - StagedDns.NewDns( - item = CustomDnsItem( - address = ipAddress, - isLocal = ipAddress.isLocalAddress() - ), - validationResult = error - ) - } + stagedDns = + if (dialog.stagedDns is StagedDns.EditDns) { + StagedDns.EditDns( + item = + CustomDnsItem( + address = ipAddress, + isLocal = ipAddress.isLocalAddress() + ), + validationResult = error, + index = dialog.stagedDns.index + ) + } else { + StagedDns.NewDns( + item = + CustomDnsItem( + address = ipAddress, + isLocal = ipAddress.isLocalAddress() + ), + validationResult = error + ) + } ) } } - fun onSaveDnsClick() = viewModelScope.launch(dispatcher) { - val dialog = - vmState.value.dialogState as? AdvancedSettingsDialogState.DnsDialog ?: return@launch + fun onSaveDnsClick() = + viewModelScope.launch(dispatcher) { + val dialog = + vmState.value.dialogState as? AdvancedSettingsDialogState.DnsDialog ?: return@launch - if (dialog.stagedDns.isValid().not()) return@launch + if (dialog.stagedDns.isValid().not()) return@launch - val updatedList = - vmState.value.customDnsList.toMutableList() - .map { it.address } - .toMutableList() - .let { activeList -> - if (dialog.stagedDns is StagedDns.EditDns) { - activeList - .apply { - set(dialog.stagedDns.index, dialog.stagedDns.item.address) - } - .asInetAddressList() - } else { - activeList - .apply { - add(dialog.stagedDns.item.address) - } - .asInetAddressList() + val updatedList = + vmState.value.customDnsList + .toMutableList() + .map { it.address } + .toMutableList() + .let { activeList -> + if (dialog.stagedDns is StagedDns.EditDns) { + activeList + .apply { + set(dialog.stagedDns.index, dialog.stagedDns.item.address) + } + .asInetAddressList() + } else { + activeList + .apply { add(dialog.stagedDns.item.address) } + .asInetAddressList() + } } - } - repository.setDnsOptions( - isCustomDnsEnabled = true, - dnsList = updatedList - ) + repository.setDnsOptions(isCustomDnsEnabled = true, dnsList = updatedList) - hideDialog() - } + hideDialog() + } - fun onToggleDnsClick(isEnabled: Boolean) = viewModelScope.launch(dispatcher) { - repository.setDnsOptions( - isEnabled, - dnsList = vmState.value.customDnsList - .map { it.address } - .asInetAddressList() - ) - } + fun onToggleDnsClick(isEnabled: Boolean) = + viewModelScope.launch(dispatcher) { + repository.setDnsOptions( + isEnabled, + dnsList = vmState.value.customDnsList.map { it.address }.asInetAddressList() + ) + } - fun onRemoveDnsClick() = viewModelScope.launch(dispatcher) { - val dialog = vmState.value.dialogState as? AdvancedSettingsDialogState.DnsDialog - ?: return@launch + fun onRemoveDnsClick() = + viewModelScope.launch(dispatcher) { + val dialog = + vmState.value.dialogState as? AdvancedSettingsDialogState.DnsDialog ?: return@launch - val updatedList = vmState.value.customDnsList.toMutableList() - .filter { - it.address != dialog.stagedDns.item.address - } - .map { it.address } - .asInetAddressList() + val updatedList = + vmState.value.customDnsList + .toMutableList() + .filter { it.address != dialog.stagedDns.item.address } + .map { it.address } + .asInetAddressList() - repository.setDnsOptions( - isCustomDnsEnabled = vmState.value.isCustomDnsEnabled && updatedList.isNotEmpty(), - dnsList = updatedList - ) + repository.setDnsOptions( + isCustomDnsEnabled = vmState.value.isCustomDnsEnabled && updatedList.isNotEmpty(), + dnsList = updatedList + ) - hideDialog() - } + hideDialog() + } private fun hideDialog() { dialogState.update { AdvancedSettingsDialogState.NoDialog } @@ -201,9 +204,7 @@ class AdvancedSettingsViewModel( private fun String.isDuplicateDns(stagedIndex: Int? = null): Boolean { return vmState.value.customDnsList - .filterIndexed { index, listItem -> - index != stagedIndex && listItem.address == this - } + .filterIndexed { index, listItem -> index != stagedIndex && listItem.address == this } .isNotEmpty() } @@ -218,10 +219,7 @@ class AdvancedSettingsViewModel( private fun List<InetAddress>.asStringAddressList(): List<CustomDnsItem> { return map { - CustomDnsItem( - address = it.hostAddress ?: EMPTY_STRING, - isLocal = it.isLocalAddress() - ) + CustomDnsItem(address = it.hostAddress ?: EMPTY_STRING, isLocal = it.isLocalAddress()) } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt index 4db0c012fd..002d478b8f 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/AdvancedSettingsViewModelState.kt @@ -11,52 +11,52 @@ data class AdvancedSettingsViewModelState( ) { fun toUiState(): AdvancedSettingsUiState { return when (dialogState) { - is AdvancedSettingsDialogState.MtuDialog -> AdvancedSettingsUiState.MtuDialogUiState( - mtu = mtuValue, - isCustomDnsEnabled = isCustomDnsEnabled, - isAllowLanEnabled = isAllowLanEnabled, - customDnsItems = customDnsList, - mtuEditValue = dialogState.mtuEditValue, - ) - is AdvancedSettingsDialogState.DnsDialog -> AdvancedSettingsUiState.DnsDialogUiState( - mtu = mtuValue, - isCustomDnsEnabled = isCustomDnsEnabled, - isAllowLanEnabled = isAllowLanEnabled, - customDnsItems = customDnsList, - stagedDns = dialogState.stagedDns, - ) - else -> AdvancedSettingsUiState.DefaultUiState( - mtu = mtuValue, - isCustomDnsEnabled = isCustomDnsEnabled, - isAllowLanEnabled = isAllowLanEnabled, - customDnsItems = customDnsList, - ) + is AdvancedSettingsDialogState.MtuDialog -> + AdvancedSettingsUiState.MtuDialogUiState( + mtu = mtuValue, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + mtuEditValue = dialogState.mtuEditValue, + ) + is AdvancedSettingsDialogState.DnsDialog -> + AdvancedSettingsUiState.DnsDialogUiState( + mtu = mtuValue, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + stagedDns = dialogState.stagedDns, + ) + else -> + AdvancedSettingsUiState.DefaultUiState( + mtu = mtuValue, + isCustomDnsEnabled = isCustomDnsEnabled, + isAllowLanEnabled = isAllowLanEnabled, + customDnsItems = customDnsList, + ) } } companion object { private const val EMPTY_STRING = "" - fun default() = AdvancedSettingsViewModelState( - mtuValue = EMPTY_STRING, - isCustomDnsEnabled = false, - customDnsList = listOf(), - isAllowLanEnabled = false, - dialogState = AdvancedSettingsDialogState.NoDialog - ) + fun default() = + AdvancedSettingsViewModelState( + mtuValue = EMPTY_STRING, + isCustomDnsEnabled = false, + customDnsList = listOf(), + isAllowLanEnabled = false, + dialogState = AdvancedSettingsDialogState.NoDialog + ) } } sealed class AdvancedSettingsDialogState { object NoDialog : AdvancedSettingsDialogState() - data class MtuDialog( - val mtuEditValue: String - ) : AdvancedSettingsDialogState() + data class MtuDialog(val mtuEditValue: String) : AdvancedSettingsDialogState() - data class DnsDialog( - val stagedDns: StagedDns - ) : AdvancedSettingsDialogState() + data class DnsDialog(val stagedDns: StagedDns) : AdvancedSettingsDialogState() } sealed interface StagedDns { @@ -83,18 +83,12 @@ sealed interface StagedDns { fun isValid() = (validationResult is ValidationResult.Success) } -data class CustomDnsItem( - val address: String, - val isLocal: Boolean -) { +data class CustomDnsItem(val address: String, val isLocal: Boolean) { companion object { private const val EMPTY_STRING = "" fun default(): CustomDnsItem { - return CustomDnsItem( - address = EMPTY_STRING, - isLocal = false - ) + return CustomDnsItem(address = EMPTY_STRING, isLocal = false) } } } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt index 003eb962ad..a8255f5675 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModel.kt @@ -15,18 +15,20 @@ class ChangelogViewModel( val changelogDialogUiState = _changelogDialogUiState.asStateFlow() fun refreshChangelogDialogUiState() { - val shouldShowChangelogDialog = alwaysShowChangelog || changelogRepository - .getVersionCodeOfMostRecentChangelogShowed() < buildVersionCode - _changelogDialogUiState.value = if (shouldShowChangelogDialog) { - val changelogList = changelogRepository.getLastVersionChanges() - if (changelogList.isNotEmpty()) { - ChangelogDialogUiState.Show(changelogList) + val shouldShowChangelogDialog = + alwaysShowChangelog || + changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersionCode + _changelogDialogUiState.value = + if (shouldShowChangelogDialog) { + val changelogList = changelogRepository.getLastVersionChanges() + if (changelogList.isNotEmpty()) { + ChangelogDialogUiState.Show(changelogList) + } else { + ChangelogDialogUiState.Hide + } } else { ChangelogDialogUiState.Hide } - } else { - ChangelogDialogUiState.Hide - } } fun dismissChangelogDialog() { diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt index d2fc044a8e..d9a682c0c8 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceListViewModel.kt @@ -41,34 +41,35 @@ class DeviceListViewModel( var accountToken: String? = null private var cachedDeviceList: List<Device>? = null - val uiState = combine( - deviceRepository.deviceList, - _stagedDeviceId, - _loadingDevices - ) { deviceList, stagedDeviceId, loadingDevices -> - val devices = if (deviceList is DeviceList.Available) { - deviceList.devices.also { cachedDeviceList = it } - } else { - cachedDeviceList - } - val deviceUiItems = devices?.sortedBy { it.creationDate }?.map { device -> - DeviceListItemUiState( - device, - loadingDevices.any { loadingDevice -> - device.id == loadingDevice - } - ) - } - val isLoading = devices == null - val stagedDevice = devices?.firstOrNull { device -> - device.id == stagedDeviceId - } - DeviceListUiState( - deviceUiItems = deviceUiItems ?: emptyList(), - isLoading = isLoading, - stagedDevice = stagedDevice - ) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), DeviceListUiState.INITIAL) + val uiState = + combine(deviceRepository.deviceList, _stagedDeviceId, _loadingDevices) { + deviceList, + stagedDeviceId, + loadingDevices -> + val devices = + if (deviceList is DeviceList.Available) { + deviceList.devices.also { cachedDeviceList = it } + } else { + cachedDeviceList + } + val deviceUiItems = + devices + ?.sortedBy { it.creationDate } + ?.map { device -> + DeviceListItemUiState( + device, + loadingDevices.any { loadingDevice -> device.id == loadingDevice } + ) + } + val isLoading = devices == null + val stagedDevice = devices?.firstOrNull { device -> device.id == stagedDeviceId } + DeviceListUiState( + deviceUiItems = deviceUiItems ?: emptyList(), + isLoading = isLoading, + stagedDevice = stagedDevice + ) + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), DeviceListUiState.INITIAL) fun stageDeviceForRemoval(deviceId: DeviceId) { _stagedDeviceId.value = deviceId @@ -85,18 +86,19 @@ class DeviceListViewModel( if (token != null && stagedDeviceId != null) { viewModelScope.launch { withContext(dispatcher) { - val result = withTimeoutOrNull(DEVICE_REMOVAL_TIMEOUT_MILLIS) { - deviceRepository.deviceRemovalEvent - .onSubscription { - clearStagedDevice() - setLoadingDevice(stagedDeviceId) - deviceRepository.removeDevice(token, stagedDeviceId) - } - .filter { (deviceId, result) -> - deviceId == stagedDeviceId && result == RemoveDeviceResult.Ok - } - .first() - } + val result = + withTimeoutOrNull(DEVICE_REMOVAL_TIMEOUT_MILLIS) { + deviceRepository.deviceRemovalEvent + .onSubscription { + clearStagedDevice() + setLoadingDevice(stagedDeviceId) + deviceRepository.removeDevice(token, stagedDeviceId) + } + .filter { (deviceId, result) -> + deviceId == stagedDeviceId && result == RemoveDeviceResult.Ok + } + .first() + } clearLoadingDevice(stagedDeviceId) @@ -118,9 +120,8 @@ class DeviceListViewModel( fun refreshDeviceState() = deviceRepository.refreshDeviceState() - fun refreshDeviceList() = accountToken?.let { token -> - deviceRepository.refreshDeviceList(token) - } + fun refreshDeviceList() = + accountToken?.let { token -> deviceRepository.refreshDeviceList(token) } private fun setLoadingDevice(deviceId: DeviceId) { _loadingDevices.value = _loadingDevices.value.toMutableList().apply { add(deviceId) } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt index db31c0610d..f5e9024c58 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/DeviceRevokedViewModel.kt @@ -23,25 +23,24 @@ class DeviceRevokedViewModel( dispatcher: CoroutineDispatcher = Dispatchers.IO ) : ViewModel() { - val uiState = serviceConnectionManager.connectionState - .map { connectionState -> connectionState.readyContainer()?.connectionProxy } - .flatMapLatest { proxy -> - proxy?.onUiStateChange - ?.callbackFlowFromSubscription(this) - ?.map { + val uiState = + serviceConnectionManager.connectionState + .map { connectionState -> connectionState.readyContainer()?.connectionProxy } + .flatMapLatest { proxy -> + proxy?.onUiStateChange?.callbackFlowFromSubscription(this)?.map { if (it.isSecured()) { DeviceRevokedUiState.SECURED } else { DeviceRevokedUiState.UNSECURED } } - ?: flowOf(DeviceRevokedUiState.UNKNOWN) - } - .stateIn( - scope = CoroutineScope(dispatcher), - started = SharingStarted.WhileSubscribed(), - initialValue = DeviceRevokedUiState.UNKNOWN - ) + ?: flowOf(DeviceRevokedUiState.UNKNOWN) + } + .stateIn( + scope = CoroutineScope(dispatcher), + started = SharingStarted.WhileSubscribed(), + initialValue = DeviceRevokedUiState.UNKNOWN + ) fun onGoToLoginClicked() { serviceConnectionManager.connectionProxy()?.let { proxy -> diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt index 259deedc57..2f59bcb347 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModel.kt @@ -28,9 +28,7 @@ class LoginViewModel( sealed class LoginUiState { object Default : LoginUiState() object Loading : LoginUiState() - data class Success( - val isOutOfTime: Boolean - ) : LoginUiState() + data class Success(val isOutOfTime: Boolean) : LoginUiState() object CreatingAccount : LoginUiState() object AccountCreated : LoginUiState() @@ -54,20 +52,22 @@ class LoginViewModel( fun createAccount() { _uiState.value = LoginUiState.CreatingAccount viewModelScope.launch(dispatcher) { - _uiState.value = accountRepository.accountCreationEvents - .onStart { accountRepository.createAccount() } - .first() - .mapToUiState() + _uiState.value = + accountRepository.accountCreationEvents + .onStart { accountRepository.createAccount() } + .first() + .mapToUiState() } } fun login(accountToken: String) { _uiState.value = LoginUiState.Loading viewModelScope.launch(dispatcher) { - _uiState.value = accountRepository.loginEvents - .onStart { accountRepository.login(accountToken) } - .map { it.result.mapToUiState(accountToken) } - .first() + _uiState.value = + accountRepository.loginEvents + .onStart { accountRepository.login(accountToken) } + .map { it.result.mapToUiState(accountToken) } + .first() } } @@ -84,12 +84,13 @@ class LoginViewModel( LoginResult.Ok -> LoginUiState.Success(false) LoginResult.InvalidAccount -> LoginUiState.InvalidAccountError LoginResult.MaxDevicesReached -> { - val refreshResult = deviceRepository.refreshAndAwaitDeviceListWithTimeout( - accountToken = accountToken, - shouldClearCache = true, - shouldOverrideCache = true, - timeoutMillis = 5000L - ) + val refreshResult = + deviceRepository.refreshAndAwaitDeviceListWithTimeout( + accountToken = accountToken, + shouldClearCache = true, + shouldOverrideCache = true, + timeoutMillis = 5000L + ) if (refreshResult.isAvailable()) { LoginUiState.TooManyDevicesError(accountToken) diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModel.kt index 9537c03a82..d2f43a77f6 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModel.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SplitTunnelingViewModel.kt @@ -35,22 +35,23 @@ class SplitTunnelingViewModel( private val excludedApps: MutableMap<String, AppData> = mutableMapOf() private val notExcludedApps: MutableMap<String, AppData> = mutableMapOf() - private val defaultListItems: List<ListItemData> = listOf( - createTextItem(R.string.split_tunneling_description) - // We will have search item in future - ) + private val defaultListItems: List<ListItemData> = + listOf( + createTextItem(R.string.split_tunneling_description) + // We will have search item in future + ) private var isSystemAppsVisible = false init { viewModelScope.launch(dispatcher) { listItemsSink.emit(defaultListItems + createDivider(0) + createProgressItem()) // this will be removed after changes on native to ignore enable parameter - if (!splitTunneling.enabled) - splitTunneling.enabled = true + if (!splitTunneling.enabled) splitTunneling.enabled = true fetchData() } viewModelScope.launch(dispatcher) { - intentFlow.shareIn(viewModelScope, SharingStarted.WhileSubscribed()) + intentFlow + .shareIn(viewModelScope, SharingStarted.WhileSubscribed()) .collect(::handleIntents) } } @@ -97,7 +98,8 @@ class SplitTunnelingViewModel( } private suspend fun fetchData() { - appsProvider.getAppsList() + appsProvider + .getAppsList() .partition { app -> splitTunneling.isAppExcluded(app.packageName) } .let { (excludedAppsList, notExcludedAppsList) -> // TODO: remove potential package names from splitTunneling list @@ -114,9 +116,10 @@ class SplitTunnelingViewModel( if (excludedApps.isNotEmpty()) { listItems += createDivider(0) listItems += createMainItem(R.string.exclude_applications) - listItems += excludedApps.values.sortedBy { it.name }.map { info -> - createApplicationItem(info, true) - } + listItems += + excludedApps.values + .sortedBy { it.name } + .map { info -> createApplicationItem(info, true) } } val shownNotExcludedApps = notExcludedApps.filter { app -> !app.value.isSystemApp || isSystemAppsVisible } @@ -124,9 +127,10 @@ class SplitTunnelingViewModel( listItems += createDivider(1) listItems += createSwitchItem(R.string.show_system_apps, isSystemAppsVisible) listItems += createMainItem(R.string.all_applications) - listItems += shownNotExcludedApps.values.sortedBy { it.name }.map { info -> - createApplicationItem(info, false) - } + listItems += + shownNotExcludedApps.values + .sortedBy { it.name } + .map { info -> createApplicationItem(info, false) } } listItemsSink.emit(listItems) } @@ -137,14 +141,14 @@ class SplitTunnelingViewModel( text = appData.name iconRes = appData.iconRes action = ListItemData.ItemAction(appData.packageName) - widget = WidgetState.ImageState( - if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add - ) + widget = + WidgetState.ImageState( + if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add + ) } - private fun createDivider(id: Int): ListItemData = ListItemData.build("space_$id") { - type = ListItemData.DIVIDER - } + private fun createDivider(id: Int): ListItemData = + ListItemData.build("space_$id") { type = ListItemData.DIVIDER } private fun createMainItem(@StringRes text: Int): ListItemData = ListItemData.build("header_$text") { @@ -159,9 +163,8 @@ class SplitTunnelingViewModel( action = ListItemData.ItemAction(text.toString()) } - private fun createProgressItem(): ListItemData = ListItemData.build(identifier = "progress") { - type = ListItemData.PROGRESS - } + private fun createProgressItem(): ListItemData = + ListItemData.build(identifier = "progress") { type = ListItemData.PROGRESS } private fun createSwitchItem(@StringRes text: Int, checked: Boolean): ListItemData = ListItemData.build(identifier = "switch_$text") { diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt b/android/app/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt index 51ed63bd0a..de56ebb878 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt @@ -12,39 +12,42 @@ import net.mullvad.talpid.util.EventNotifier class ConnectivityListener { private val availableNetworks = HashSet<Network>() - private val callback = object : NetworkCallback() { - override fun onAvailable(network: Network) { - availableNetworks.add(network) - isConnected = true - } + private val callback = + object : NetworkCallback() { + override fun onAvailable(network: Network) { + availableNetworks.add(network) + isConnected = true + } - override fun onLost(network: Network) { - availableNetworks.remove(network) - isConnected = !availableNetworks.isEmpty() + override fun onLost(network: Network) { + availableNetworks.remove(network) + isConnected = !availableNetworks.isEmpty() + } } - } private lateinit var connectivityManager: ConnectivityManager val connectivityNotifier = EventNotifier(false) - var isConnected by observable(false) { _, oldValue, newValue -> - if (newValue != oldValue) { - if (senderAddress != 0L) { - notifyConnectivityChange(newValue, senderAddress) - } + var isConnected by + observable(false) { _, oldValue, newValue -> + if (newValue != oldValue) { + if (senderAddress != 0L) { + notifyConnectivityChange(newValue, senderAddress) + } - connectivityNotifier.notify(newValue) + connectivityNotifier.notify(newValue) + } } - } var senderAddress = 0L fun register(context: Context) { - val request = NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - .build() + val request = + NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .build() connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/CreateTunResult.kt b/android/app/src/main/kotlin/net/mullvad/talpid/CreateTunResult.kt index 150382bb1a..33f62026d6 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/CreateTunResult.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/CreateTunResult.kt @@ -11,10 +11,8 @@ sealed class CreateTunResult { get() = true } - class InvalidDnsServers( - val addresses: ArrayList<InetAddress>, - val tunFd: Int - ) : CreateTunResult() { + class InvalidDnsServers(val addresses: ArrayList<InetAddress>, val tunFd: Int) : + CreateTunResult() { override val isOpen get() = true } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/app/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt index 4203d0b0a1..9f5e4a67a6 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt @@ -10,17 +10,19 @@ import net.mullvad.mullvadvpn.util.SdkUtils.setMeteredIfSupported import net.mullvad.talpid.tun_provider.TunConfig open class TalpidVpnService : VpnService() { - private var activeTunStatus by observable<CreateTunResult?>(null) { _, oldTunStatus, _ -> - val oldTunFd = when (oldTunStatus) { - is CreateTunResult.Success -> oldTunStatus.tunFd - is CreateTunResult.InvalidDnsServers -> oldTunStatus.tunFd - else -> null - } + private var activeTunStatus by + observable<CreateTunResult?>(null) { _, oldTunStatus, _ -> + val oldTunFd = + when (oldTunStatus) { + is CreateTunResult.Success -> oldTunStatus.tunFd + is CreateTunResult.InvalidDnsServers -> oldTunStatus.tunFd + else -> null + } - if (oldTunFd != null) { - ParcelFileDescriptor.adoptFd(oldTunFd).close() + if (oldTunFd != null) { + ParcelFileDescriptor.adoptFd(oldTunFd).close() + } } - } private val tunIsOpen get() = activeTunStatus?.isOpen ?: false @@ -59,9 +61,7 @@ open class TalpidVpnService : VpnService() { } fun createTun() { - synchronized(this) { - activeTunStatus = createTun(currentTunConfig) - } + synchronized(this) { activeTunStatus = createTun(currentTunConfig) } } fun recreateTunIfOpen(config: TunConfig) { @@ -74,15 +74,11 @@ open class TalpidVpnService : VpnService() { } fun closeTun() { - synchronized(this) { - activeTunStatus = null - } + synchronized(this) { activeTunStatus = null } } fun markTunAsStale() { - synchronized(this) { - tunIsStale = true - } + synchronized(this) { tunIsStale = true } } private fun createTun(config: TunConfig): CreateTunResult { @@ -93,32 +89,33 @@ open class TalpidVpnService : VpnService() { var invalidDnsServerAddresses = ArrayList<InetAddress>() - val builder = Builder().apply { - for (address in config.addresses) { - addAddress(address, prefixForAddress(address)) - } + val builder = + Builder().apply { + for (address in config.addresses) { + addAddress(address, prefixForAddress(address)) + } - for (dnsServer in config.dnsServers) { - try { - addDnsServer(dnsServer) - } catch (exception: IllegalArgumentException) { - invalidDnsServerAddresses.add(dnsServer) + for (dnsServer in config.dnsServers) { + try { + addDnsServer(dnsServer) + } catch (exception: IllegalArgumentException) { + invalidDnsServerAddresses.add(dnsServer) + } } - } - for (route in config.routes) { - addRoute(route.address, route.prefixLength.toInt()) - } + for (route in config.routes) { + addRoute(route.address, route.prefixLength.toInt()) + } - disallowedApps?.let { apps -> - for (app in apps) { - addDisallowedApplication(app) + disallowedApps?.let { apps -> + for (app in apps) { + addDisallowedApplication(app) + } } + setMtu(config.mtu) + setBlocking(false) + setMeteredIfSupported(false) } - setMtu(config.mtu) - setBlocking(false) - setMeteredIfSupported(false) - } val vpnInterface = builder.establish() val tunFd = vpnInterface?.detachFd() diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt b/android/app/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt index 5efb1bcb1c..89fdedaba1 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/net/TransportProtocol.kt @@ -5,5 +5,6 @@ import kotlinx.parcelize.Parcelize @Parcelize enum class TransportProtocol : Parcelable { - Tcp, Udp + Tcp, + Udp } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt b/android/app/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt index 5c081b392e..7bb9eb2530 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/net/TunnelEndpoint.kt @@ -4,7 +4,4 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class TunnelEndpoint( - val endpoint: Endpoint, - val quantumResistant: Boolean -) : Parcelable +data class TunnelEndpoint(val endpoint: Endpoint, val quantumResistant: Boolean) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt index 365ac0811b..a62abaacd0 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ActionAfterDisconnect.kt @@ -5,5 +5,7 @@ import kotlinx.parcelize.Parcelize @Parcelize enum class ActionAfterDisconnect : Parcelable { - Nothing, Block, Reconnect + Nothing, + Block, + Reconnect } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt index 2c5ba00bf5..070d190beb 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt @@ -3,5 +3,4 @@ package net.mullvad.talpid.tunnel import android.os.Parcelable import kotlinx.parcelize.Parcelize -@Parcelize -data class ErrorState(val cause: ErrorStateCause, val isBlocking: Boolean) : Parcelable +@Parcelize data class ErrorState(val cause: ErrorStateCause, val isBlocking: Boolean) : Parcelable diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt index b31f71f1fb..5096e5c693 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt @@ -14,27 +14,19 @@ sealed class ErrorStateCause : Parcelable { } } - @Parcelize - object Ipv6Unavailable : ErrorStateCause() + @Parcelize object Ipv6Unavailable : ErrorStateCause() - @Parcelize - object SetFirewallPolicyError : ErrorStateCause() + @Parcelize object SetFirewallPolicyError : ErrorStateCause() - @Parcelize - object SetDnsError : ErrorStateCause() + @Parcelize object SetDnsError : ErrorStateCause() - @Parcelize - class InvalidDnsServers(val addresses: ArrayList<InetAddress>) : ErrorStateCause() + @Parcelize class InvalidDnsServers(val addresses: ArrayList<InetAddress>) : ErrorStateCause() - @Parcelize - object StartTunnelError : ErrorStateCause() + @Parcelize object StartTunnelError : ErrorStateCause() - @Parcelize - class TunnelParameterError(val error: ParameterGenerationError) : ErrorStateCause() + @Parcelize class TunnelParameterError(val error: ParameterGenerationError) : ErrorStateCause() - @Parcelize - object IsOffline : ErrorStateCause() + @Parcelize object IsOffline : ErrorStateCause() - @Parcelize - object VpnPermissionDenied : ErrorStateCause() + @Parcelize object VpnPermissionDenied : ErrorStateCause() } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ParameterGenerationError.kt b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ParameterGenerationError.kt index 51fa8ac461..b1504c676f 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ParameterGenerationError.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/tunnel/ParameterGenerationError.kt @@ -1,5 +1,8 @@ package net.mullvad.talpid.tunnel enum class ParameterGenerationError { - NoMatchingRelay, NoMatchingBridgeRelay, NoWireguardKey, CustomTunnelHostResultionError + NoMatchingRelay, + NoMatchingBridgeRelay, + NoWireguardKey, + CustomTunnelHostResultionError } diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifier.kt b/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifier.kt index fb038f1243..148b56eb45 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifier.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifier.kt @@ -52,20 +52,14 @@ class EventNotifier<T>(private val initialValue: T) { } fun unsubscribe(id: Any) { - synchronized(this) { - listeners.remove(id) - } + synchronized(this) { listeners.remove(id) } } fun unsubscribeAll() { - synchronized(this) { - listeners.clear() - } + synchronized(this) { listeners.clear() } } - fun notifiable() = observable(latestEvent) { _, _, newValue -> - notify(newValue) - } + fun notifiable() = observable(latestEvent) { _, _, newValue -> notify(newValue) } } fun <T> autoSubscribable(id: Any, fallback: T, listener: (T) -> Unit) = diff --git a/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifierExtensions.kt b/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifierExtensions.kt index 454cae6133..add362fcb1 100644 --- a/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifierExtensions.kt +++ b/android/app/src/main/kotlin/net/mullvad/talpid/util/EventNotifierExtensions.kt @@ -4,10 +4,6 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow fun <T> EventNotifier<T>.callbackFlowFromSubscription(id: Any) = callbackFlow { - this@callbackFlowFromSubscription.subscribe(id) { - this.trySend(it) - } - awaitClose { - this@callbackFlowFromSubscription.unsubscribe(id) - } + this@callbackFlowFromSubscription.subscribe(id) { this.trySend(it) } + awaitClose { this@callbackFlowFromSubscription.unsubscribe(id) } } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt index 1acdf9e577..1f84171e3d 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestCoroutineRule.kt @@ -7,9 +7,8 @@ import kotlinx.coroutines.test.setMain import org.junit.rules.TestWatcher import org.junit.runner.Description -class TestCoroutineRule( - val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher() -) : TestWatcher() { +class TestCoroutineRule(val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()) : + TestWatcher() { override fun starting(description: Description?) { super.starting(description) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt index 4c4f043c06..59e1c70c6c 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/TestUtils.kt @@ -2,10 +2,15 @@ package net.mullvad.mullvadvpn import kotlin.test.assertTrue -fun <T> assertLists(expected: List<T>, actual: List<T>, message: String? = null) = assertTrue( - expected.size == actual.size && expected.containsAll(actual) && actual.containsAll(expected), - message ?: """Expected list should have same size and contains same items. +fun <T> assertLists(expected: List<T>, actual: List<T>, message: String? = null) = + assertTrue( + expected.size == actual.size && + expected.containsAll(actual) && + actual.containsAll(expected), + message + ?: """Expected list should have same size and contains same items. | Expected(${expected.size}): $expected | Actual(${actual.size}) : $actual - """.trimMargin() -) + """ + .trimMargin() + ) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt index e6d43621a1..8db700f452 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsIconManagerTest.kt @@ -58,12 +58,8 @@ class ApplicationsIconManagerTest { assertEquals(mockedDrawable, result) assertEquals(mockedDrawable, result2) - verify(exactly = 2) { - mockedMainLooper.isCurrentThread - } - verify(exactly = 1) { - mockedPackageManager.getApplicationIcon(testPackageName) - } + verify(exactly = 2) { mockedMainLooper.isCurrentThread } + verify(exactly = 1) { mockedPackageManager.getApplicationIcon(testPackageName) } } @Test diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt index e1a9e37ac4..3f2534798d 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/applist/ApplicationsProviderTest.kt @@ -34,26 +34,41 @@ class ApplicationsProviderTest { every { mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA) - } returns listOf( - createApplicationInfo(launchWithInternetPackageName, launch = true, internet = true), - createApplicationInfo(launchWithoutInternetPackageName, launch = true), - createApplicationInfo(nonLaunchWithInternetPackageName, internet = true), - createApplicationInfo(nonLaunchWithoutInternetPackageName), - createApplicationInfo( - leanbackLaunchWithInternetPackageName, - leanback = true, - internet = true - ), - createApplicationInfo(leanbackLaunchWithoutInternetPackageName, leanback = true), - createApplicationInfo(selfPackageName, internet = true, launch = true) - ) + } returns + listOf( + createApplicationInfo( + launchWithInternetPackageName, + launch = true, + internet = true + ), + createApplicationInfo(launchWithoutInternetPackageName, launch = true), + createApplicationInfo(nonLaunchWithInternetPackageName, internet = true), + createApplicationInfo(nonLaunchWithoutInternetPackageName), + createApplicationInfo( + leanbackLaunchWithInternetPackageName, + leanback = true, + internet = true + ), + createApplicationInfo(leanbackLaunchWithoutInternetPackageName, leanback = true), + createApplicationInfo(selfPackageName, internet = true, launch = true) + ) val result = testSubject.getAppsList() - val expected = listOf( - AppData(launchWithInternetPackageName, 0, launchWithInternetPackageName), - AppData(nonLaunchWithInternetPackageName, 0, nonLaunchWithInternetPackageName, true), - AppData(leanbackLaunchWithInternetPackageName, 0, leanbackLaunchWithInternetPackageName) - ) + val expected = + listOf( + AppData(launchWithInternetPackageName, 0, launchWithInternetPackageName), + AppData( + nonLaunchWithInternetPackageName, + 0, + nonLaunchWithInternetPackageName, + true + ), + AppData( + leanbackLaunchWithInternetPackageName, + 0, + leanbackLaunchWithInternetPackageName + ) + ) assertLists(expected, result) @@ -61,31 +76,34 @@ class ApplicationsProviderTest { mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA) listOf( - launchWithInternetPackageName, - launchWithoutInternetPackageName, - nonLaunchWithInternetPackageName, - nonLaunchWithoutInternetPackageName, - leanbackLaunchWithInternetPackageName, - leanbackLaunchWithoutInternetPackageName, - selfPackageName - ).forEach { packageName -> - mockedPackageManager.checkPermission(internet, packageName) - } + launchWithInternetPackageName, + launchWithoutInternetPackageName, + nonLaunchWithInternetPackageName, + nonLaunchWithoutInternetPackageName, + leanbackLaunchWithInternetPackageName, + leanbackLaunchWithoutInternetPackageName, + selfPackageName + ) + .forEach { packageName -> + mockedPackageManager.checkPermission(internet, packageName) + } listOf( - launchWithInternetPackageName, - nonLaunchWithInternetPackageName, - leanbackLaunchWithInternetPackageName - ).forEach { packageName -> - mockedPackageManager.getLaunchIntentForPackage(packageName) - } + launchWithInternetPackageName, + nonLaunchWithInternetPackageName, + leanbackLaunchWithInternetPackageName + ) + .forEach { packageName -> + mockedPackageManager.getLaunchIntentForPackage(packageName) + } listOf( - nonLaunchWithInternetPackageName, - leanbackLaunchWithInternetPackageName, - ).forEach { packageName -> - mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) - } + nonLaunchWithInternetPackageName, + leanbackLaunchWithInternetPackageName, + ) + .forEach { packageName -> + mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) + } } } @@ -103,26 +121,16 @@ class ApplicationsProviderTest { every { mockApplicationInfo.loadLabel(mockedPackageManager) } returns packageName - every { - mockedPackageManager.getLaunchIntentForPackage(packageName) - } returns if (launch || systemApp) - mockk() - else - null + every { mockedPackageManager.getLaunchIntentForPackage(packageName) } returns + if (launch || systemApp) mockk() else null - every { - mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) - } returns if (leanback || systemApp) - mockk() - else - null + every { mockedPackageManager.getLeanbackLaunchIntentForPackage(packageName) } returns + if (leanback || systemApp) mockk() else null every { mockedPackageManager.checkPermission(Manifest.permission.INTERNET, packageName) - } returns if (internet) - PackageManager.PERMISSION_GRANTED - else - PackageManager.PERMISSION_DENIED + } returns + if (internet) PackageManager.PERMISSION_GRANTED else PackageManager.PERMISSION_DENIED return mockApplicationInfo } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/UiModuleTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/UiModuleTest.kt index 92ffd792a0..01b1807d42 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/UiModuleTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/di/UiModuleTest.kt @@ -18,10 +18,7 @@ import org.koin.test.KoinTestRule class UiModuleTest : KoinTest { - @get:Rule - val koinTestRule = KoinTestRule.create { - modules(uiModule) - } + @get:Rule val koinTestRule = KoinTestRule.create { modules(uiModule) } @After fun tearDown() { @@ -31,18 +28,17 @@ class UiModuleTest : KoinTest { @Test fun test_scope_linking() { val appsScope: Scope = getKoin().createScope(APPS_SCOPE, named(APPS_SCOPE)) - val serviceConnectionScope = getKoin().createScope( - SERVICE_CONNECTION_SCOPE, - named(SERVICE_CONNECTION_SCOPE) - ) + val serviceConnectionScope = + getKoin().createScope(SERVICE_CONNECTION_SCOPE, named(SERVICE_CONNECTION_SCOPE)) appsScope.linkTo(serviceConnectionScope) val mockedMessenger = mockk<Messenger>() val mockedEventMessageHandler = mockk<MessageDispatcher<Event>>(relaxed = true) - val serviceConnectionSplitTunneling = serviceConnectionScope.get<SplitTunneling>( - parameters = { parametersOf(mockedMessenger, mockedEventMessageHandler) } - ) + val serviceConnectionSplitTunneling = + serviceConnectionScope.get<SplitTunneling>( + parameters = { parametersOf(mockedMessenger, mockedEventMessageHandler) } + ) assertEquals(appsScope.get<SplitTunneling>(), serviceConnectionSplitTunneling) } diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxyTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxyTest.kt index 44ccdf013b..ff10770540 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxyTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ConnectionProxyTest.kt @@ -26,14 +26,11 @@ import org.junit.Test class ConnectionProxyTest { - @MockK - private lateinit var mockedMainLooper: Looper + @MockK private lateinit var mockedMainLooper: Looper - @MockK - private lateinit var connection: Messenger + @MockK private lateinit var connection: Messenger - @MockK - private lateinit var mockedDispatchingHandler: EventDispatcher + @MockK private lateinit var mockedDispatchingHandler: EventDispatcher lateinit var connectionProxy: ConnectionProxy @Before @@ -57,9 +54,7 @@ class ConnectionProxyTest { fun test_initialize_connection_proxy() { // Arrange val eventType = slot<KClass<Event.TunnelStateChange>>() - every { - mockedDispatchingHandler.registerHandler(capture(eventType), any()) - } just Runs + every { mockedDispatchingHandler.registerHandler(capture(eventType), any()) } just Runs // Create ConnectionProxy instance and assert initial Event type connectionProxy = ConnectionProxy(connection, mockedDispatchingHandler) assertEquals(Event.TunnelStateChange::class, eventType.captured.java.kotlin) @@ -69,9 +64,7 @@ class ConnectionProxyTest { fun test_tunnel_normal_connect_disconnect() { // Arrange every { connection.send(any()) } just Runs - every { - mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) - } just Runs + every { mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) } just Runs // Act and Assert no crashes connectionProxy = ConnectionProxy(connection, mockedDispatchingHandler) connectionProxy.connect() @@ -82,9 +75,7 @@ class ConnectionProxyTest { fun test_tunnel_handle_crash_on_connect() { // Arrange every { connection.send(any()) } throws DeadObjectException() - every { - mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) - } just Runs + every { mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) } just Runs // Act and Assert no crashes connectionProxy = ConnectionProxy(connection, mockedDispatchingHandler) connectionProxy.connect() diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ServiceConnectionDeviceDataSourceTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ServiceConnectionDeviceDataSourceTest.kt index 8b737cba69..81633c89be 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ServiceConnectionDeviceDataSourceTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/service/ServiceConnectionDeviceDataSourceTest.kt @@ -25,14 +25,11 @@ import org.junit.Test class ServiceConnectionDeviceDataSourceTest { private val tracker = JobTracker() - @MockK - private lateinit var mockedMainLooper: Looper + @MockK private lateinit var mockedMainLooper: Looper - @MockK - private lateinit var mockedDispatchingHandler: EventDispatcher + @MockK private lateinit var mockedDispatchingHandler: EventDispatcher - @MockK - private lateinit var connection: Messenger + @MockK private lateinit var connection: Messenger lateinit var serviceConnectionDeviceDataSource: ServiceConnectionDeviceDataSource @@ -57,9 +54,7 @@ class ServiceConnectionDeviceDataSourceTest { fun test_get_devices_list() { // Arrange every { connection.send(any()) } just Runs - every { - mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) - } just Runs + every { mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) } just Runs // Act and Assert no crashes serviceConnectionDeviceDataSource = ServiceConnectionDeviceDataSource(connection, mockedDispatchingHandler) @@ -70,9 +65,7 @@ class ServiceConnectionDeviceDataSourceTest { fun test_catch_exception_on_devices_list() { // Arrange every { connection.send(any()) } throws DeadObjectException() - every { - mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) - } just Runs + every { mockedDispatchingHandler.registerHandler(any<KClass<Event>>(), any()) } just Runs // Act and Assert no crashes serviceConnectionDeviceDataSource = ServiceConnectionDeviceDataSource(connection, mockedDispatchingHandler) diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt index eafac23bfc..3547b92065 100644 --- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt +++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt @@ -19,8 +19,7 @@ import org.junit.Test class ChangelogViewModelTest { - @MockK - private lateinit var mockedChangelogRepository: ChangelogRepository + @MockK private lateinit var mockedChangelogRepository: ChangelogRepository private lateinit var viewModel: ChangelogViewModel @@ -28,9 +27,8 @@ class ChangelogViewModelTest { fun setup() { MockKAnnotations.init(this) mockkStatic(EVENT_NOTIFIER_EXTENSION_CLASS) - every { - mockedChangelogRepository.setVersionCodeOfMostRecentChangelogShowed(any()) - } just Runs + every { mockedChangelogRepository.setVersionCodeOfMostRecentChangelogShowed(any()) } just + Runs viewModel = ChangelogViewModel(mockedChangelogRepository, 1, false) } @@ -52,9 +50,8 @@ class ChangelogViewModelTest { viewModel.changelogDialogUiState.test { // Arrange val fakeList = listOf("test") - every { - mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() - } returns -1 + every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns + -1 every { mockedChangelogRepository.getLastVersionChanges() } returns fakeList // Assert initial ui state @@ -76,9 +73,8 @@ class ChangelogViewModelTest { viewModel.changelogDialogUiState.test { // Arrange val fakeEmptyList = emptyList<String>() - every { - mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() - } returns -1 + every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns + -1 every { mockedChangelogRepository.getLastVersionChanges() } returns fakeEmptyList // Assert initial ui state 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 c3be82c1e1..32cce31136 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 @@ -30,11 +30,9 @@ import org.junit.Test class DeviceRevokedViewModelTest { - @MockK - private lateinit var mockedAccountRepository: AccountRepository + @MockK private lateinit var mockedAccountRepository: AccountRepository - @MockK - private lateinit var mockedServiceConnectionManager: ServiceConnectionManager + @MockK private lateinit var mockedServiceConnectionManager: ServiceConnectionManager private val serviceConnectionState = MutableStateFlow<ServiceConnectionState>(ServiceConnectionState.Disconnected) @@ -46,11 +44,12 @@ class DeviceRevokedViewModelTest { MockKAnnotations.init(this) mockkStatic(EVENT_NOTIFIER_EXTENSION_CLASS) every { mockedServiceConnectionManager.connectionState } returns serviceConnectionState - viewModel = DeviceRevokedViewModel( - mockedServiceConnectionManager, - mockedAccountRepository, - TestCoroutineDispatcher() - ) + viewModel = + DeviceRevokedViewModel( + mockedServiceConnectionManager, + mockedAccountRepository, + TestCoroutineDispatcher() + ) } @After @@ -79,17 +78,19 @@ class DeviceRevokedViewModelTest { @Test fun testUiStateWhenServiceConnectedAndReady() = runBlockingTest { // Arrange - val mockedContainer = mockk<ServiceConnectionContainer>().apply { - val eventNotifierMock = mockk<EventNotifier<TunnelState>>().apply { - every { callbackFlowFromSubscription(any()) } returns MutableStateFlow( - TunnelState.Connected(mockk(), mockk()) - ) + val mockedContainer = + mockk<ServiceConnectionContainer>().apply { + val eventNotifierMock = + mockk<EventNotifier<TunnelState>>().apply { + every { callbackFlowFromSubscription(any()) } returns + MutableStateFlow(TunnelState.Connected(mockk(), mockk())) + } + val mockedConnectionProxy = + mockk<ConnectionProxy>().apply { + every { onUiStateChange } returns eventNotifierMock + } + every { connectionProxy } returns mockedConnectionProxy } - val mockedConnectionProxy = mockk<ConnectionProxy>().apply { - every { onUiStateChange } returns eventNotifierMock - } - every { connectionProxy } returns mockedConnectionProxy - } // Act, Assert viewModel.uiState.test { @@ -102,30 +103,30 @@ class DeviceRevokedViewModelTest { @Test fun testGoToLoginWhenDisconnected() { // Arrange - val mockedContainer = mockk<ServiceConnectionContainer>().also { - every { it.connectionProxy.state } returns TunnelState.Disconnected - every { it.connectionProxy.disconnect() } just Runs - every { mockedAccountRepository.logout() } just Runs - } + val mockedContainer = + mockk<ServiceConnectionContainer>().also { + every { it.connectionProxy.state } returns TunnelState.Disconnected + every { it.connectionProxy.disconnect() } just Runs + every { mockedAccountRepository.logout() } just Runs + } serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockedContainer) // Act viewModel.onGoToLoginClicked() // Assert - verify { - mockedAccountRepository.logout() - } + verify { mockedAccountRepository.logout() } } @Test fun testGoToLoginWhenConnected() { // Arrange - val mockedContainer = mockk<ServiceConnectionContainer>().also { - every { it.connectionProxy.state } returns TunnelState.Connected(mockk(), mockk()) - every { it.connectionProxy.disconnect() } just Runs - every { mockedAccountRepository.logout() } just Runs - } + val mockedContainer = + mockk<ServiceConnectionContainer>().also { + every { it.connectionProxy.state } returns TunnelState.Connected(mockk(), mockk()) + every { it.connectionProxy.disconnect() } just Runs + every { mockedAccountRepository.logout() } just Runs + } serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockedContainer) // Act 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 ea35dd7c6c..0531342cc4 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 @@ -28,14 +28,11 @@ import org.junit.Test class LoginViewModelTest { - @MockK - private lateinit var mockedAccountRepository: AccountRepository + @MockK private lateinit var mockedAccountRepository: AccountRepository - @MockK - private lateinit var mockedDeviceRepository: DeviceRepository + @MockK private lateinit var mockedDeviceRepository: DeviceRepository - @MockK - private lateinit var mockedServiceConnectionContainer: ServiceConnectionContainer + @MockK private lateinit var mockedServiceConnectionContainer: ServiceConnectionContainer private lateinit var loginViewModel: LoginViewModel @@ -58,11 +55,12 @@ class LoginViewModelTest { serviceConnectionState.value = ServiceConnectionState.ConnectedReady(mockedServiceConnectionContainer) - loginViewModel = LoginViewModel( - mockedAccountRepository, - mockedDeviceRepository, - TestCoroutineDispatcher() - ) + loginViewModel = + LoginViewModel( + mockedAccountRepository, + mockedDeviceRepository, + TestCoroutineDispatcher() + ) } @Test @@ -109,15 +107,8 @@ class LoginViewModelTest { @Test fun testLoginWithTooManyDevicesError() = runBlockingTest { coEvery { - mockedDeviceRepository.refreshAndAwaitDeviceListWithTimeout( - any(), - any(), - any(), - any() - ) - } returns DeviceListEvent.Available( - DUMMY_ACCOUNT_TOKEN, listOf() - ) + mockedDeviceRepository.refreshAndAwaitDeviceListWithTimeout(any(), any(), any(), any()) + } returns DeviceListEvent.Available(DUMMY_ACCOUNT_TOKEN, listOf()) loginViewModel.uiState.test { skipDefaultItem() @@ -125,7 +116,8 @@ class LoginViewModelTest { assertEquals(LoginViewModel.LoginUiState.Loading, awaitItem()) loginTestEvents.emit(Event.LoginEvent(LoginResult.MaxDevicesReached)) assertEquals( - LoginViewModel.LoginUiState.TooManyDevicesError(DUMMY_ACCOUNT_TOKEN), awaitItem() + LoginViewModel.LoginUiState.TooManyDevicesError(DUMMY_ACCOUNT_TOKEN), + awaitItem() ) } } 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 ac229ba3fb..9fa0e9ea88 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 @@ -29,11 +29,9 @@ import org.junit.Test import org.junit.rules.Timeout class SplitTunnelingViewModelTest { - @get:Rule - val testCoroutineRule = TestCoroutineRule() + @get:Rule val testCoroutineRule = TestCoroutineRule() - @get:Rule - val timeout = Timeout(3000L, TimeUnit.MILLISECONDS) + @get:Rule val timeout = Timeout(3000L, TimeUnit.MILLISECONDS) private val mockedApplicationsProvider = mockk<ApplicationsProvider>() private val mockedSplitTunneling = mockk<SplitTunneling>() private lateinit var testSubject: SplitTunnelingViewModel @@ -50,166 +48,174 @@ class SplitTunnelingViewModelTest { } @Test - fun test_has_progress_on_start() = runBlockingTest(testCoroutineRule.testDispatcher) { - initTestSubject(emptyList()) - val actualList: List<ListItemData> = testSubject.listItems.first() + fun test_has_progress_on_start() = + runBlockingTest(testCoroutineRule.testDispatcher) { + initTestSubject(emptyList()) + val actualList: List<ListItemData> = testSubject.listItems.first() - val initialExpectedList = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(0), - createProgressItem() - ) + val initialExpectedList = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createProgressItem() + ) - assertLists(initialExpectedList, actualList) + assertLists(initialExpectedList, actualList) - verify(exactly = 1) { - mockedApplicationsProvider.getAppsList() + verify(exactly = 1) { mockedApplicationsProvider.getAppsList() } } - } @Test - fun test_empty_app_list() = runBlockingTest(testCoroutineRule.testDispatcher) { - initTestSubject(emptyList()) - testSubject.processIntent(ViewIntent.ViewIsReady) - val actualList = testSubject.listItems.first() - val expectedList = listOf(createTextItem(R.string.split_tunneling_description)) - assertLists(expectedList, actualList) - } + fun test_empty_app_list() = + runBlockingTest(testCoroutineRule.testDispatcher) { + initTestSubject(emptyList()) + testSubject.processIntent(ViewIntent.ViewIsReady) + val actualList = testSubject.listItems.first() + val expectedList = listOf(createTextItem(R.string.split_tunneling_description)) + assertLists(expectedList, actualList) + } @Test - fun test_apps_list_delivered() = runBlockingTest(testCoroutineRule.testDispatcher) { - val appExcluded = AppData("test.excluded", 0, "testName1") - val appNotExcluded = AppData("test.not.excluded", 0, "testName2") - every { mockedSplitTunneling.isAppExcluded(appExcluded.packageName) } returns true - every { mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) } returns false + fun test_apps_list_delivered() = + runBlockingTest(testCoroutineRule.testDispatcher) { + val appExcluded = AppData("test.excluded", 0, "testName1") + val appNotExcluded = AppData("test.not.excluded", 0, "testName2") + every { mockedSplitTunneling.isAppExcluded(appExcluded.packageName) } returns true + every { mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) } returns false - initTestSubject(listOf(appExcluded, appNotExcluded)) - testSubject.processIntent(ViewIntent.ViewIsReady) + initTestSubject(listOf(appExcluded, appNotExcluded)) + testSubject.processIntent(ViewIntent.ViewIsReady) - val actualList = testSubject.listItems.first() - val expectedList = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(0), - createMainItem(R.string.exclude_applications), - createApplicationItem(appExcluded, true), - createDivider(1), - createSwitchItem(R.string.show_system_apps, false), - createMainItem(R.string.all_applications), - createApplicationItem(appNotExcluded, false), - ) + val actualList = testSubject.listItems.first() + val expectedList = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(appExcluded, true), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(appNotExcluded, false), + ) - assertLists(expectedList, actualList) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.isAppExcluded(appExcluded.packageName) - mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) + assertLists(expectedList, actualList) + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(appExcluded.packageName) + mockedSplitTunneling.isAppExcluded(appNotExcluded.packageName) + } } - } @Test - fun test_remove_app_from_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) { - val app = AppData("test", 0, "testName") - every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns true - every { mockedSplitTunneling.includeApp(app.packageName) } just Runs + fun test_remove_app_from_excluded() = + runBlockingTest(testCoroutineRule.testDispatcher) { + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns true + every { mockedSplitTunneling.includeApp(app.packageName) } just Runs - initTestSubject(listOf(app)) - testSubject.processIntent(ViewIntent.ViewIsReady) + initTestSubject(listOf(app)) + testSubject.processIntent(ViewIntent.ViewIsReady) - val listBeforeAction = testSubject.listItems.first() - val expectedListBeforeAction = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(0), - createMainItem(R.string.exclude_applications), - createApplicationItem(app, true), - ) + val listBeforeAction = testSubject.listItems.first() + val expectedListBeforeAction = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(app, true), + ) - assertLists(expectedListBeforeAction, listBeforeAction) + assertLists(expectedListBeforeAction, listBeforeAction) - val item = listBeforeAction.first { it.identifier == app.packageName } - testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) + val item = listBeforeAction.first { it.identifier == app.packageName } + testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) - val itemsAfterAction = testSubject.listItems.first() - val expectedList = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(1), - createSwitchItem(R.string.show_system_apps, false), - createMainItem(R.string.all_applications), - createApplicationItem(app, false), - ) + val itemsAfterAction = testSubject.listItems.first() + val expectedList = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(app, false), + ) - assertLists(expectedList, itemsAfterAction) + assertLists(expectedList, itemsAfterAction) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.isAppExcluded(app.packageName) - mockedSplitTunneling.includeApp(app.packageName) + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(app.packageName) + mockedSplitTunneling.includeApp(app.packageName) + } } - } @Test - fun test_add_app_to_excluded() = runBlockingTest(testCoroutineRule.testDispatcher) { - val app = AppData("test", 0, "testName") - every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns false - every { mockedSplitTunneling.excludeApp(app.packageName) } just Runs - initTestSubject(listOf(app)) - testSubject.processIntent(ViewIntent.ViewIsReady) + fun test_add_app_to_excluded() = + runBlockingTest(testCoroutineRule.testDispatcher) { + val app = AppData("test", 0, "testName") + every { mockedSplitTunneling.isAppExcluded(app.packageName) } returns false + every { mockedSplitTunneling.excludeApp(app.packageName) } just Runs + initTestSubject(listOf(app)) + testSubject.processIntent(ViewIntent.ViewIsReady) - val listBeforeAction = testSubject.listItems.first() - val expectedListBeforeAction = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(1), - createSwitchItem(R.string.show_system_apps, false), - createMainItem(R.string.all_applications), - createApplicationItem(app, false), - ) + val listBeforeAction = testSubject.listItems.first() + val expectedListBeforeAction = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(1), + createSwitchItem(R.string.show_system_apps, false), + createMainItem(R.string.all_applications), + createApplicationItem(app, false), + ) - assertLists(expectedListBeforeAction, listBeforeAction) + assertLists(expectedListBeforeAction, listBeforeAction) - val item = listBeforeAction.first { it.identifier == app.packageName } - testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) + val item = listBeforeAction.first { it.identifier == app.packageName } + testSubject.processIntent(ViewIntent.ChangeApplicationGroup(item)) - val itemsAfterAction = testSubject.listItems.first() - val expectedList = listOf( - createTextItem(R.string.split_tunneling_description), - createDivider(0), - createMainItem(R.string.exclude_applications), - createApplicationItem(app, true), - ) + val itemsAfterAction = testSubject.listItems.first() + val expectedList = + listOf( + createTextItem(R.string.split_tunneling_description), + createDivider(0), + createMainItem(R.string.exclude_applications), + createApplicationItem(app, true), + ) - assertLists(expectedList, itemsAfterAction) + assertLists(expectedList, itemsAfterAction) - verifyAll { - mockedSplitTunneling.enabled - mockedSplitTunneling.isAppExcluded(app.packageName) - mockedSplitTunneling.excludeApp(app.packageName) + verifyAll { + mockedSplitTunneling.enabled + mockedSplitTunneling.isAppExcluded(app.packageName) + mockedSplitTunneling.excludeApp(app.packageName) + } } - } private fun initTestSubject(appList: List<AppData>) { every { mockedApplicationsProvider.getAppsList() } returns appList - testSubject = SplitTunnelingViewModel( - mockedApplicationsProvider, - mockedSplitTunneling, - testCoroutineRule.testDispatcher - ) + testSubject = + SplitTunnelingViewModel( + mockedApplicationsProvider, + mockedSplitTunneling, + testCoroutineRule.testDispatcher + ) } - private fun createApplicationItem( - appData: AppData, - checked: Boolean - ): ListItemData = ListItemData.build(appData.packageName) { - type = ListItemData.APPLICATION - text = appData.name - iconRes = appData.iconRes - action = ListItemData.ItemAction(appData.packageName) - widget = WidgetState.ImageState( - if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add - ) - } + private fun createApplicationItem(appData: AppData, checked: Boolean): ListItemData = + ListItemData.build(appData.packageName) { + type = ListItemData.APPLICATION + text = appData.name + iconRes = appData.iconRes + action = ListItemData.ItemAction(appData.packageName) + widget = + WidgetState.ImageState( + if (checked) R.drawable.ic_icons_remove else R.drawable.ic_icons_add + ) + } - private fun createDivider(id: Int): ListItemData = ListItemData.build("space_$id") { - type = ListItemData.DIVIDER - } + private fun createDivider(id: Int): ListItemData = + ListItemData.build("space_$id") { type = ListItemData.DIVIDER } private fun createMainItem(@StringRes text: Int): ListItemData = ListItemData.build("header_$text") { @@ -224,9 +230,8 @@ class SplitTunnelingViewModelTest { action = ListItemData.ItemAction(text.toString()) } - private fun createProgressItem(): ListItemData = ListItemData.build(identifier = "progress") { - type = ListItemData.PROGRESS - } + private fun createProgressItem(): ListItemData = + ListItemData.build(identifier = "progress") { type = ListItemData.PROGRESS } private fun createSwitchItem(@StringRes text: Int, checked: Boolean): ListItemData = ListItemData.build(identifier = "switch_$text") { diff --git a/android/lib/endpoint/src/debug/kotlin/net/mullvad/mullvadvpn/lib/endpoint/CustomApiEndpointConfiguration.kt b/android/lib/endpoint/src/debug/kotlin/net/mullvad/mullvadvpn/lib/endpoint/CustomApiEndpointConfiguration.kt index b3a00c809c..5fb8db5fe1 100644 --- a/android/lib/endpoint/src/debug/kotlin/net/mullvad/mullvadvpn/lib/endpoint/CustomApiEndpointConfiguration.kt +++ b/android/lib/endpoint/src/debug/kotlin/net/mullvad/mullvadvpn/lib/endpoint/CustomApiEndpointConfiguration.kt @@ -3,8 +3,6 @@ package net.mullvad.mullvadvpn.lib.endpoint import kotlinx.parcelize.Parcelize @Parcelize -data class CustomApiEndpointConfiguration( - val apiEndpoint: ApiEndpoint -) : ApiEndpointConfiguration { +data class CustomApiEndpointConfiguration(val apiEndpoint: ApiEndpoint) : ApiEndpointConfiguration { override fun apiEndpoint() = apiEndpoint } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt index 59905d72e2..19cc5a8b93 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/extension/UiAutomatorExtensions.kt @@ -22,10 +22,7 @@ fun UiDevice.findObjectWithTimeout( timeout: Long = DEFAULT_INTERACTION_TIMEOUT ): UiObject2 { - wait( - Until.hasObject(selector), - timeout - ) + wait(Until.hasObject(selector), timeout) return try { findObject(selector) @@ -50,10 +47,7 @@ fun UiDevice.clickAllowOnNotificationPermissionPromptIfApiLevel31AndAbove( val selector = By.text("Allow") - wait( - Until.hasObject(selector), - timeout - ) + wait(Until.hasObject(selector), timeout) try { findObjectWithTimeout(selector).click() @@ -69,10 +63,7 @@ fun UiObject2.findObjectWithTimeout( timeout: Long = DEFAULT_INTERACTION_TIMEOUT ): UiObject2 { - wait( - Until.hasObject(selector), - timeout - ) + wait(Until.hasObject(selector), timeout) return try { findObject(selector) diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt index d3b2abe7ae..d5a2e5176b 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/interactor/AppInteractor.kt @@ -20,10 +20,7 @@ import net.mullvad.mullvadvpn.test.common.extension.clickAgreeOnPrivacyDisclaime import net.mullvad.mullvadvpn.test.common.extension.clickAllowOnNotificationPermissionPromptIfApiLevel31AndAbove import net.mullvad.mullvadvpn.test.common.extension.findObjectWithTimeout -class AppInteractor( - private val device: UiDevice, - private val targetContext: Context -) { +class AppInteractor(private val device: UiDevice, private val targetContext: Context) { fun launch(customApiEndpointConfiguration: CustomApiEndpointConfiguration? = null) { device.pressHome() // Wait for launcher @@ -41,10 +38,7 @@ class AppInteractor( } } targetContext.startActivity(intent) - device.wait( - Until.hasObject(By.pkg(MULLVAD_PACKAGE).depth(0)), - APP_LAUNCH_TIMEOUT - ) + device.wait(Until.hasObject(By.pkg(MULLVAD_PACKAGE).depth(0)), APP_LAUNCH_TIMEOUT) } fun launchAndEnsureLoggedIn(accountToken: String) { @@ -56,8 +50,10 @@ class AppInteractor( } fun attemptLogin(accountToken: String) { - val loginObject = device.findObjectWithTimeout(By.clazz("android.widget.EditText")) - .apply { text = accountToken } + val loginObject = + device.findObjectWithTimeout(By.clazz("android.widget.EditText")).apply { + text = accountToken + } loginObject.parent.findObject(By.clazz(ImageButton::class.java)).click() } @@ -67,10 +63,10 @@ class AppInteractor( fun extractIpAddress(): String { device.findObjectWithTimeout(By.res(TUNNEL_INFO_ID)).click() - return device.findObjectWithTimeout( - By.res(TUNNEL_OUT_ADDRESS_ID), - CONNECTION_TIMEOUT - ).text.extractIpAddress() + return device + .findObjectWithTimeout(By.res(TUNNEL_OUT_ADDRESS_ID), CONNECTION_TIMEOUT) + .text + .extractIpAddress() } fun clickSettingsCog() { @@ -85,9 +81,7 @@ class AppInteractor( device.findObjectWithTimeout(By.text(text)).click() } - fun waitForLoginPrompt( - timeout: Long = LOGIN_PROMPT_TIMEOUT - ) { + fun waitForLoginPrompt(timeout: Long = LOGIN_PROMPT_TIMEOUT) { device.findObjectWithTimeout(By.text("Login"), timeout) } diff --git a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt index 138d09cc28..c7b8992292 100644 --- a/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt +++ b/android/test/common/src/main/kotlin/net/mullvad/mullvadvpn/test/common/rule/CaptureScreenshotOnFailedTestRule.kt @@ -60,10 +60,7 @@ class CaptureScreenshotOnFailedTestRule(private val testTag: String) : TestWatch ) { contentValues.apply { put(MediaStore.MediaColumns.DISPLAY_NAME, filename) - put( - MediaStore.Images.Media.RELATIVE_PATH, - "$DIRECTORY_PICTURES/$baseDir" - ) + put(MediaStore.Images.Media.RELATIVE_PATH, "$DIRECTORY_PICTURES/$baseDir") } val uri = @@ -89,14 +86,17 @@ class CaptureScreenshotOnFailedTestRule(private val testTag: String) : TestWatch baseDir: String, filename: String ) { - val screenshotBaseDirectory = Paths.get( - Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES).path, - baseDir, - ).toFile().apply { - if (exists().not()) { - mkdirs() - } - } + val screenshotBaseDirectory = + Paths.get( + Environment.getExternalStoragePublicDirectory(DIRECTORY_PICTURES).path, + baseDir, + ) + .toFile() + .apply { + if (exists().not()) { + mkdirs() + } + } FileOutputStream(File(screenshotBaseDirectory, filename)).use { outputStream -> try { this.compress(Bitmap.CompressFormat.JPEG, 50, outputStream) @@ -107,8 +107,9 @@ class CaptureScreenshotOnFailedTestRule(private val testTag: String) : TestWatch contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) } - private fun createBaseScreenshotContentValues() = ContentValues().apply { - put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") - put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) - } + private fun createBaseScreenshotContentValues() = + ContentValues().apply { + put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") + put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()) + } } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt index 8f72fef0be..488162b08c 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/ConnectionTest.kt @@ -12,13 +12,9 @@ import org.junit.Test class ConnectionTest : EndToEndTest() { - @Rule - @JvmField - val cleanupAccountTestRule = CleanupAccountTestRule() + @Rule @JvmField val cleanupAccountTestRule = CleanupAccountTestRule() - @Rule - @JvmField - val forgetAllVpnAppsInSettingsTestRule = ForgetAllVpnAppsInSettingsTestRule() + @Rule @JvmField val forgetAllVpnAppsInSettingsTestRule = ForgetAllVpnAppsInSettingsTestRule() @Test fun testConnectAndVerifyWithConnectionCheck() { diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt index f8f5bb8f6c..3514405dd9 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/EndToEndTest.kt @@ -19,16 +19,15 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) abstract class EndToEndTest { - @Rule - @JvmField - val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG) + @Rule @JvmField val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG) @Rule @JvmField - val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( - Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE - ) + val permissionRule: GrantPermissionRule = + GrantPermissionRule.grant( + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE + ) lateinit var device: UiDevice lateinit var targetContext: Context @@ -41,14 +40,13 @@ abstract class EndToEndTest { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) targetContext = InstrumentationRegistry.getInstrumentation().targetContext - validTestAccountToken = InstrumentationRegistry.getArguments() - .getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) - invalidTestAccountToken = InstrumentationRegistry.getArguments() - .getRequiredArgument(INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) + validTestAccountToken = + InstrumentationRegistry.getArguments() + .getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) + invalidTestAccountToken = + InstrumentationRegistry.getArguments() + .getRequiredArgument(INVALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) - app = AppInteractor( - device, - targetContext - ) + app = AppInteractor(device, targetContext) } } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt index 9cec30d872..646af7a18e 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/LoginTest.kt @@ -15,9 +15,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class LoginTest : EndToEndTest() { - @Rule - @JvmField - val cleanupAccountTestRule = CleanupAccountTestRule() + @Rule @JvmField val cleanupAccountTestRule = CleanupAccountTestRule() @Test fun testLoginWithInvalidCredentials() { diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt index 9e5f0aa665..bcf4adcc9e 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/interactor/SystemSettingsInteractor.kt @@ -7,18 +7,15 @@ import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import net.mullvad.mullvadvpn.test.common.extension.findObjectByCaseInsensitiveText -class SystemSettingsInteractor( - private val uiDevice: UiDevice, - private val context: Context -) { +class SystemSettingsInteractor(private val uiDevice: UiDevice, private val context: Context) { fun openVpnSettings() { - val intent = Intent("com.intent.MAIN").apply { - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - } - intent.component = ComponentName.unflattenFromString( - "com.android.settings/.Settings\$VpnSettingsActivity" - ) + val intent = + Intent("com.intent.MAIN").apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + intent.component = + ComponentName.unflattenFromString("com.android.settings/.Settings\$VpnSettingsActivity") context.startActivity(intent) Thread.sleep(1000) } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/CleanupAccountTestRule.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/CleanupAccountTestRule.kt index 2b69436f6d..2e19cb42fe 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/CleanupAccountTestRule.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/CleanupAccountTestRule.kt @@ -13,8 +13,9 @@ class CleanupAccountTestRule : TestWatcher() { override fun starting(description: Description) { Log.d(LOG_TAG, "Cleaning up account before test: ${description.methodName}") val targetContext = InstrumentationRegistry.getInstrumentation().targetContext - val validTestAccountToken = InstrumentationRegistry.getArguments() - .getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) + val validTestAccountToken = + InstrumentationRegistry.getArguments() + .getRequiredArgument(VALID_TEST_ACCOUNT_TOKEN_ARGUMENT_KEY) MullvadAccountInteractor(SimpleMullvadHttpClient(targetContext), validTestAccountToken) .cleanupAccount() } diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/ConnCheckState.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/ConnCheckState.kt index 744e80124e..75004270a4 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/ConnCheckState.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/ConnCheckState.kt @@ -1,6 +1,3 @@ package net.mullvad.mullvadvpn.test.e2e.misc -data class ConnCheckState( - val isConnected: Boolean, - val ipAddress: String -) +data class ConnCheckState(val isConnected: Boolean, val ipAddress: String) diff --git a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt index bc56737b04..b97fb28f45 100644 --- a/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt +++ b/android/test/e2e/src/main/kotlin/net/mullvad/mullvadvpn/test/e2e/misc/SimpleMullvadHttpClient.kt @@ -23,17 +23,13 @@ class SimpleMullvadHttpClient(context: Context) { Log.v(LOG_TAG, "Remove all devices") val token = login(accountToken) val devices = getDeviceList(token) - devices.forEach { - removeDevice(token, it) - } + devices.forEach { removeDevice(token, it) } Log.v(LOG_TAG, "All devices removed") } fun login(accountToken: String): String { Log.v(LOG_TAG, "Attempt login with account token: $accountToken") - val json = JSONObject().apply { - put("account_number", accountToken) - } + val json = JSONObject().apply { put("account_number", accountToken) } return sendSimpleSynchronousRequest(Request.Method.POST, AUTH_URL, json)!!.let { response -> response.getString("access_token").also { accessToken -> Log.v(LOG_TAG, "Successfully logged in and received access token: $accessToken") @@ -44,21 +40,20 @@ class SimpleMullvadHttpClient(context: Context) { fun getDeviceList(accessToken: String): List<String> { Log.v(LOG_TAG, "Get devices") - val response = sendSimpleSynchronousRequestArray( - Request.Method.GET, - DEVICE_LIST_URL, - token = accessToken - ) + val response = + sendSimpleSynchronousRequestArray( + Request.Method.GET, + DEVICE_LIST_URL, + token = accessToken + ) - return response!!.iterator<JSONObject>().asSequence().toList() + return response!! + .iterator<JSONObject>() + .asSequence() + .toList() .also { - it - .map { jsonObject -> - jsonObject.getString("name") - } - .also { deviceNames -> - Log.v(LOG_TAG, "Devices received: $deviceNames") - } + it.map { jsonObject -> jsonObject.getString("name") } + .also { deviceNames -> Log.v(LOG_TAG, "Devices received: $deviceNames") } } .map { it.getString("id") } .toList() @@ -74,13 +69,8 @@ class SimpleMullvadHttpClient(context: Context) { } fun runConnectionCheck(): ConnCheckState? { - return sendSimpleSynchronousRequestString( - Request.Method.GET, - CONN_CHECK_URL - ) - ?.let { respose -> - JSONObject(respose) - } + return sendSimpleSynchronousRequestString(Request.Method.GET, CONN_CHECK_URL) + ?.let { respose -> JSONObject(respose) } ?.let { json -> ConnCheckState( isConnected = json.getBoolean("mullvad_exit_ip"), @@ -96,24 +86,19 @@ class SimpleMullvadHttpClient(context: Context) { token: String? = null ): JSONObject? { val future = RequestFuture.newFuture<JSONObject>() - val request = object : JsonObjectRequest( - method, - url, - body, - future, - future - ) { - override fun getHeaders(): MutableMap<String, String> { - val headers = HashMap<String, String>() - if (body != null) { - headers.put("Content-Type", "application/json") - } - if (token != null) { - headers.put("Authorization", "Bearer $token") + val request = + object : JsonObjectRequest(method, url, body, future, future) { + override fun getHeaders(): MutableMap<String, String> { + val headers = HashMap<String, String>() + if (body != null) { + headers.put("Content-Type", "application/json") + } + if (token != null) { + headers.put("Authorization", "Bearer $token") + } + return headers } - return headers } - } queue.add(request) return try { future.get().also { response -> @@ -132,28 +117,22 @@ class SimpleMullvadHttpClient(context: Context) { token: String? = null ): String? { val future = RequestFuture.newFuture<String>() - val request = object : StringRequest( - method, - url, - future, - future - ) { - override fun getHeaders(): MutableMap<String, String> { - val headers = HashMap<String, String>() - if (body != null) { - headers.put("Content-Type", "application/json") - } - if (token != null) { - headers.put("Authorization", "Bearer $token") + val request = + object : StringRequest(method, url, future, future) { + override fun getHeaders(): MutableMap<String, String> { + val headers = HashMap<String, String>() + if (body != null) { + headers.put("Content-Type", "application/json") + } + if (token != null) { + headers.put("Authorization", "Bearer $token") + } + return headers } - return headers } - } queue.add(request) return try { - future.get().also { response -> - Log.v(LOG_TAG, "String request response: $response") - } + future.get().also { response -> Log.v(LOG_TAG, "String request response: $response") } } catch (e: Exception) { Log.v(LOG_TAG, "String request error: ${e.message}") throw TestEventException(REQUEST_ERROR_MESSAGE) @@ -167,22 +146,17 @@ class SimpleMullvadHttpClient(context: Context) { token: String? = null ): JSONArray? { val future = RequestFuture.newFuture<JSONArray>() - val request = object : JsonArrayRequest( - method, - url, - null, - future, - future - ) { - override fun getHeaders(): MutableMap<String, String> { - val headers = HashMap<String, String>() - headers.put("Content-Type", "application/json") - if (token != null) { - headers.put("Authorization", "Bearer $token") + val request = + object : JsonArrayRequest(method, url, null, future, future) { + override fun getHeaders(): MutableMap<String, String> { + val headers = HashMap<String, String>() + headers.put("Content-Type", "application/json") + if (token != null) { + headers.put("Authorization", "Bearer $token") + } + return headers } - return headers } - } queue.add(request) return try { future.get().also { response -> diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt index e3708a47f1..06f7164034 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiDispatcher.kt @@ -29,8 +29,10 @@ class MockApiDispatcher : Dispatcher() { AUTH_TOKEN_URL_PATH -> handleLoginRequest(request.body) DEVICES_URL_PATH -> { when (request.method) { - "get", "GET" -> handleDeviceListRequest() - "post", "POST" -> handleDeviceCreationRequest(request.body) + "get", + "GET" -> handleDeviceListRequest() + "post", + "POST" -> handleDeviceCreationRequest(request.body) else -> MockResponse().setResponseCode(404) } } @@ -51,9 +53,10 @@ class MockApiDispatcher : Dispatcher() { .addJsonHeader() .setBody( accessTokenJsonResponse( - accessToken = DUMMY_ACCESS_TOKEN, - expiry = currentUtcTimeWithOffsetZero().plusDays(1) - ).toString() + accessToken = DUMMY_ACCESS_TOKEN, + expiry = currentUtcTimeWithOffsetZero().plusDays(1) + ) + .toString() ) } else { Log.e( @@ -69,13 +72,9 @@ class MockApiDispatcher : Dispatcher() { MockResponse() .setResponseCode(200) .addJsonHeader() - .setBody( - accountInfoJson( - id = DUMMY_ID, - expiry = expiry - ).toString() - ) - } ?: MockResponse().setResponseCode(400) + .setBody(accountInfoJson(id = DUMMY_ID, expiry = expiry).toString()) + } + ?: MockResponse().setResponseCode(400) } private fun handleDeviceInfoRequest(): MockResponse { @@ -85,33 +84,36 @@ class MockApiDispatcher : Dispatcher() { .addJsonHeader() .setBody( deviceJson( - id = DUMMY_ID, - name = DUMMY_DEVICE_NAME, - publicKey = cachedKey, - creationDate = currentUtcTimeWithOffsetZero().minusDays(1) - ).toString() + id = DUMMY_ID, + name = DUMMY_DEVICE_NAME, + publicKey = cachedKey, + creationDate = currentUtcTimeWithOffsetZero().minusDays(1) + ) + .toString() ) - } ?: MockResponse().setResponseCode(400) + } + ?: MockResponse().setResponseCode(400) } private fun handleDeviceCreationRequest(body: Buffer): MockResponse { - return body.getPubKey() - .also { newKey -> - cachedPubKeyFromAppUnderTest = newKey - } + return body + .getPubKey() + .also { newKey -> cachedPubKeyFromAppUnderTest = newKey } ?.let { newKey -> MockResponse() .setResponseCode(201) .addJsonHeader() .setBody( deviceJson( - id = DUMMY_ID, - name = DUMMY_DEVICE_NAME, - publicKey = newKey, - creationDate = currentUtcTimeWithOffsetZero().minusDays(1) - ).toString() + id = DUMMY_ID, + name = DUMMY_DEVICE_NAME, + publicKey = newKey, + creationDate = currentUtcTimeWithOffsetZero().minusDays(1) + ) + .toString() ) - } ?: MockResponse().setResponseCode(400) + } + ?: MockResponse().setResponseCode(400) } private fun handleDeviceListRequest(): MockResponse { @@ -120,15 +122,18 @@ class MockApiDispatcher : Dispatcher() { .setResponseCode(200) .addJsonHeader() .setBody( - JSONArray().put( - deviceJson( - id = DUMMY_ID, - name = DUMMY_DEVICE_NAME, - publicKey = cachedKey, - creationDate = currentUtcTimeWithOffsetZero().minusDays(1) + JSONArray() + .put( + deviceJson( + id = DUMMY_ID, + name = DUMMY_DEVICE_NAME, + publicKey = cachedKey, + creationDate = currentUtcTimeWithOffsetZero().minusDays(1) + ) ) - ).toString() + .toString() ) - } ?: MockResponse().setResponseCode(400) + } + ?: MockResponse().setResponseCode(400) } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt index ba5ba01810..bb5c20eebb 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/MockApiTest.kt @@ -24,21 +24,15 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) abstract class MockApiTest { - @Rule - @JvmField - val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG) + @Rule @JvmField val rule = CaptureScreenshotOnFailedTestRule(LOG_TAG) @Rule @JvmField - val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( - WRITE_EXTERNAL_STORAGE, - READ_EXTERNAL_STORAGE - ) + val permissionRule: GrantPermissionRule = + GrantPermissionRule.grant(WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE) protected val apiDispatcher = MockApiDispatcher() - private val mockWebServer = MockWebServer().apply { - dispatcher = apiDispatcher - } + private val mockWebServer = MockWebServer().apply { dispatcher = apiDispatcher } lateinit var device: UiDevice lateinit var targetContext: Context @@ -50,10 +44,7 @@ abstract class MockApiTest { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) targetContext = InstrumentationRegistry.getInstrumentation().targetContext - app = AppInteractor( - device, - targetContext - ) + app = AppInteractor(device, targetContext) mockWebServer.start() Log.d(LOG_TAG, "Mocked web server started using port: ${mockWebServer.port}") @@ -66,16 +57,14 @@ abstract class MockApiTest { } private fun createEndpoint(port: Int): CustomApiEndpointConfiguration { - val mockApiSocket = InetSocketAddress( - InetAddress.getLocalHost(), - port - ) - val api = ApiEndpoint( - address = mockApiSocket, - disableAddressCache = true, - disableTls = true, - forceDirectConnection = true - ) + val mockApiSocket = InetSocketAddress(InetAddress.getLocalHost(), port) + val api = + ApiEndpoint( + address = mockApiSocket, + disableAddressCache = true, + disableTls = true, + forceDirectConnection = true + ) return CustomApiEndpointConfiguration(api) } } diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt index 145cbafbd2..b76c4d4278 100644 --- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt +++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/util/JsonUtils.kt @@ -5,38 +5,30 @@ import org.joda.time.DateTime import org.json.JSONArray import org.json.JSONObject -fun accountInfoJson( - id: String, - expiry: DateTime -) = JSONObject().apply { - put("id", id) - put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339()) - put("max_ports", 5) - put("can_add_ports", true) - put("max_devices", 5) - put("can_add_devices", true) -} +fun accountInfoJson(id: String, expiry: DateTime) = + JSONObject().apply { + put("id", id) + put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339()) + put("max_ports", 5) + put("can_add_ports", true) + put("max_devices", 5) + put("can_add_devices", true) + } -fun deviceJson( - id: String, - name: String, - publicKey: String, - creationDate: DateTime -) = JSONObject().apply { - put("id", id) - put("name", name) - put("pubkey", publicKey) - put("hijack_dns", true) - put("created", creationDate.formatStrictlyAccordingToIso8601AndRfc3339()) - put("ipv4_address", "127.0.0.1/32") - put("ipv6_address", "fc00::1/128") - put("ports", JSONArray()) -} +fun deviceJson(id: String, name: String, publicKey: String, creationDate: DateTime) = + JSONObject().apply { + put("id", id) + put("name", name) + put("pubkey", publicKey) + put("hijack_dns", true) + put("created", creationDate.formatStrictlyAccordingToIso8601AndRfc3339()) + put("ipv4_address", "127.0.0.1/32") + put("ipv6_address", "fc00::1/128") + put("ports", JSONArray()) + } -fun accessTokenJsonResponse( - accessToken: String, - expiry: DateTime -) = JSONObject().apply { - put("access_token", accessToken) - put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339()) -} +fun accessTokenJsonResponse(accessToken: String, expiry: DateTime) = + JSONObject().apply { + put("access_token", accessToken) + put("expiry", expiry.formatStrictlyAccordingToIso8601AndRfc3339()) + } |
