diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 979eb466e2f0..4d5ab430caf2 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -47,9 +47,9 @@ android {
lint {
lintConfig = file("${rootProject.projectDir}/config/lint.xml")
- baseline = file("lint-baseline.xml")
abortOnError = true
warningsAsErrors = true
+ checkDependencies = true
}
}
diff --git a/android/app/lint-baseline.xml b/android/app/lint-baseline.xml
deleted file mode 100644
index 9f67e68efad6..000000000000
--- a/android/app/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
index cfe2c7c4dfdb..f428b876c743 100644
--- a/android/app/src/debug/AndroidManifest.xml
+++ b/android/app/src/debug/AndroidManifest.xml
@@ -11,7 +11,7 @@
android:theme="@style/Theme.App.Starting"
android:extractNativeLibs="true"
android:allowBackup="false"
- android:banner="@drawable/banner"
+ android:banner="@mipmap/ic_banner"
android:name=".MullvadApplication"
tools:ignore="DataExtractionRules,GoogleAppIndexingWarning"/>
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8dc3c4e38551..24c430f44457 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,7 +3,10 @@
-
+
+
@@ -20,9 +23,12 @@
android:required="false" />
+
MaterialTheme.colorScheme.error
- StatusLevel.Warning -> MaterialTheme.colorScheme.warning
- StatusLevel.Info -> MaterialTheme.colorScheme.surfaceContainer
- },
- shape = CircleShape,
- )
- .size(Dimens.notificationStatusIconSize)
- .constrainAs(status) {
- top.linkTo(textTitle.top)
- start.linkTo(parent.start)
- bottom.linkTo(textTitle.bottom)
- }
+ NotificationDot(
+ statusLevel,
+ Modifier.constrainAs(status) {
+ top.linkTo(textTitle.top)
+ start.linkTo(parent.start)
+ bottom.linkTo(textTitle.bottom)
+ },
)
Text(
text = title.uppercase(),
@@ -173,24 +164,58 @@ private fun Notification(notificationBannerData: NotificationData) {
)
}
action?.let {
- IconButton(
+ NotificationAction(
+ it.icon,
+ onClick = it.onClick,
+ contentDescription = it.contentDescription,
modifier =
Modifier.constrainAs(actionIcon) {
- top.linkTo(parent.top)
- end.linkTo(parent.end)
- bottom.linkTo(parent.bottom)
- }
- .testTag(NOTIFICATION_BANNER_ACTION)
- .padding(all = Dimens.notificationEndIconPadding),
- onClick = it.onClick,
- ) {
- Icon(
- modifier = Modifier.padding(Dimens.notificationIconPadding),
- imageVector = it.icon,
- contentDescription = it.contentDescription,
- tint = MaterialTheme.colorScheme.onSurface,
- )
- }
+ top.linkTo(parent.top)
+ end.linkTo(parent.end)
+ bottom.linkTo(parent.bottom)
+ },
+ )
}
}
}
+
+@Composable
+private fun NotificationDot(statusLevel: StatusLevel, modifier: Modifier) {
+ Box(
+ modifier =
+ modifier
+ .background(
+ color =
+ when (statusLevel) {
+ StatusLevel.Error -> MaterialTheme.colorScheme.error
+ StatusLevel.Warning -> MaterialTheme.colorScheme.warning
+ StatusLevel.Info -> MaterialTheme.colorScheme.surfaceContainer
+ },
+ shape = CircleShape,
+ )
+ .size(Dimens.notificationStatusIconSize)
+ )
+}
+
+@Composable
+private fun NotificationAction(
+ imageVector: ImageVector,
+ contentDescription: String?,
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ IconButton(
+ modifier =
+ modifier
+ .testTag(NOTIFICATION_BANNER_ACTION)
+ .padding(all = Dimens.notificationEndIconPadding),
+ onClick = onClick,
+ ) {
+ Icon(
+ modifier = Modifier.padding(Dimens.notificationIconPadding),
+ imageVector = imageVector,
+ contentDescription = contentDescription,
+ tint = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
index 3d49a7afcac9..04f3c05a11e0 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/dialog/RedeemVoucherDialog.kt
@@ -41,6 +41,7 @@ import net.mullvad.mullvadvpn.compose.textfield.CustomTextField
import net.mullvad.mullvadvpn.compose.util.MAX_VOUCHER_LENGTH
import net.mullvad.mullvadvpn.compose.util.vouchersVisualTransformation
import net.mullvad.mullvadvpn.constant.VOUCHER_LENGTH
+import net.mullvad.mullvadvpn.lib.model.DAYS_PER_VOUCHER_MONTH
import net.mullvad.mullvadvpn.lib.model.RedeemVoucherError
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
@@ -162,18 +163,18 @@ fun RedeemVoucherDialog(
val message =
stringResource(
R.string.added_to_your_account,
- when (days) {
- 0 -> {
+ when {
+ days == 0 -> {
stringResource(R.string.less_than_one_day)
}
- in 1..59 -> {
+ days < 2 * DAYS_PER_VOUCHER_MONTH -> {
pluralStringResource(id = R.plurals.days, count = days, days)
}
else -> {
pluralStringResource(
id = R.plurals.months,
- count = days / 30,
- days / 30,
+ count = days / DAYS_PER_VOUCHER_MONTH,
+ days / DAYS_PER_VOUCHER_MONTH,
)
}
},
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
index 11b41dd27a61..9611897e766c 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/extensions/ResourcesExtensions.kt
@@ -2,20 +2,20 @@ package net.mullvad.mullvadvpn.compose.extensions
import android.content.res.Resources
import net.mullvad.mullvadvpn.R
-import org.joda.time.DateTime
import org.joda.time.Duration
-import org.joda.time.Period
+
+private const val DAYS_IN_STANDARD_YEAR = 365
fun Resources.getExpiryQuantityString(accountExpiry: Duration): String {
- val expiryPeriod = Period(DateTime.now(), accountExpiry)
+ val days = accountExpiry.standardDays.toInt()
+ val years = (accountExpiry.standardDays / DAYS_IN_STANDARD_YEAR).toInt()
+
return if (accountExpiry.millis <= 0) {
getString(R.string.out_of_time)
- } else if (expiryPeriod.years > 0) {
- getRemainingText(this, R.plurals.years_left, expiryPeriod.years)
- } else if (expiryPeriod.months >= 3) {
- getRemainingText(this, R.plurals.months_left, expiryPeriod.months)
- } else if (expiryPeriod.months > 0 || expiryPeriod.days >= 1) {
- getRemainingText(this, R.plurals.days_left, expiryPeriod.days)
+ } else if (years > 1) {
+ getRemainingText(this, R.plurals.years_left, years)
+ } else if (days >= 1) {
+ getRemainingText(this, R.plurals.days_left, days)
} else {
getString(R.string.less_than_a_day_left)
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
index 9dcd01676760..7a3d3380228a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/LoginScreen.kt
@@ -94,6 +94,9 @@ private fun PreviewLoginScreen(
AppTheme { LoginScreen(state = state) }
}
+private const val TOP_SPACER_WEIGHT = 1f
+private const val BOTTOM_SPACER_WEIGHT = 3f
+
@Destination(style = LoginTransition::class)
@Composable
fun Login(
@@ -177,7 +180,7 @@ private fun LoginScreen(
.background(MaterialTheme.colorScheme.primary)
.verticalScroll(scrollState)
) {
- Spacer(modifier = Modifier.weight(1f))
+ Spacer(modifier = Modifier.weight(TOP_SPACER_WEIGHT))
LoginIcon(
state.loginState,
modifier =
@@ -185,7 +188,7 @@ private fun LoginScreen(
.padding(bottom = Dimens.largePadding),
)
LoginContent(state, onAccountNumberChange, onLoginClick, onDeleteHistoryClick)
- Spacer(modifier = Modifier.weight(3f))
+ Spacer(modifier = Modifier.weight(BOTTOM_SPACER_WEIGHT))
CreateAccountPanel(onCreateAccountClick, isEnabled = state.loginState is Idle)
}
}
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 b64de576eeb9..5bdcc961e77a 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
@@ -13,7 +13,6 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -23,8 +22,6 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.constant.EMPTY_STRING
import net.mullvad.mullvadvpn.constant.NEWLINE_STRING
@@ -53,9 +50,6 @@ fun CustomTextField(
imeAction = ImeAction.Done,
),
) {
-
- val scope = rememberCoroutineScope()
-
// This is the same implementation as in BasicTextField.kt but with initial selection set at the
// end of the text rather than in the beginning.
// This is a fix for https://issuetracker.google.com/issues/272693535.
@@ -97,16 +91,7 @@ fun CustomTextField(
singleLine = true,
placeholder = placeholderText?.let { { Text(text = it) } },
keyboardOptions = keyboardOptions,
- keyboardActions =
- KeyboardActions(
- onDone = {
- scope.launch {
- // https://issuetracker.google.com/issues/305518328
- delay(100)
- onSubmit(value)
- }
- }
- ),
+ keyboardActions = KeyboardActions(onDone = { onSubmit(value) }),
visualTransformation = visualTransformation,
colors = colors,
isError = !isValidValue,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
index 4d85ae986842..48e4ac85acfc 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/EditApiAccessMethodViewModel.kt
@@ -38,6 +38,7 @@ import net.mullvad.mullvadvpn.repository.ApiAccessRepository
import net.mullvad.mullvadvpn.util.delayAtLeast
import org.apache.commons.validator.routines.InetAddressValidator
+@Suppress("TooManyFunctions")
class EditApiAccessMethodViewModel(
private val apiAccessRepository: ApiAccessRepository,
private val inetAddressValidator: InetAddressValidator,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
index c34b182aa673..4ddad8477bae 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
@@ -43,6 +43,7 @@ import net.mullvad.mullvadvpn.usecase.customlists.CustomListActionUseCase
import net.mullvad.mullvadvpn.usecase.customlists.CustomListsRelayItemUseCase
import net.mullvad.mullvadvpn.usecase.customlists.FilterCustomListsRelayItemUseCase
+@Suppress("TooManyFunctions")
class SelectLocationViewModel(
private val relayListFilterRepository: RelayListFilterRepository,
private val availableProvidersUseCase: AvailableProvidersUseCase,
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
index 7829d0ce24cc..5ca136341eed 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnSettingsViewModel.kt
@@ -43,6 +43,7 @@ sealed interface VpnSettingsSideEffect {
data object NavigateToDnsDialog : VpnSettingsSideEffect
}
+@Suppress("TooManyFunctions")
class VpnSettingsViewModel(
private val repository: SettingsRepository,
private val relayListRepository: RelayListRepository,
diff --git a/android/app/src/main/res/xml/data_extraction_rules.xml b/android/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000000..b762ef705c10
--- /dev/null
+++ b/android/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/xml/full_backup_content.xml b/android/app/src/main/res/xml/full_backup_content.xml
new file mode 100644
index 000000000000..e88e6597d9a6
--- /dev/null
+++ b/android/app/src/main/res/xml/full_backup_content.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
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 560dafb24a42..efa97c6ab0f9 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
@@ -1,6 +1,7 @@
package net.mullvad.mullvadvpn.applist
import android.Manifest
+import android.annotation.SuppressLint
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import io.mockk.every
@@ -22,6 +23,7 @@ class ApplicationsProviderTest {
unmockkAll()
}
+ @SuppressLint("UseCheckPermission")
@Test
fun `fetch all apps should work`() {
val launchWithInternetPackageName = "launch_with_internet_package_name"
@@ -75,6 +77,7 @@ class ApplicationsProviderTest {
verifyAll {
mockedPackageManager.getInstalledApplications(PackageManager.GET_META_DATA)
+ // Ensure checkPermission was invoked on all packages
listOf(
launchWithInternetPackageName,
launchWithoutInternetPackageName,
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
index f62e00c87eaf..24bcb0e4d0a7 100644
--- a/android/build.gradle.kts
+++ b/android/build.gradle.kts
@@ -71,7 +71,6 @@ buildscript {
}
}
-val baselineFile = file("$rootDir/config/baseline.xml")
val configFile = files("$rootDir/config/detekt.yml")
val projectSource = file(projectDir)
@@ -82,7 +81,6 @@ detekt {
allRules = false
config.setFrom(configFile)
source.setFrom(projectSource)
- baseline = baselineFile
parallel = true
ignoreFailures = false
autoCorrect = true
diff --git a/android/config/baseline.xml b/android/config/baseline.xml
deleted file mode 100644
index 34abe7b92498..000000000000
--- a/android/config/baseline.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- CyclomaticComplexMethod:MockApiDispatcher.kt$MockApiDispatcher$override fun dispatch(request: RecordedRequest): MockResponse
- EmptyKtFile:build.gradle.kts$.build.gradle.kts
- LongMethod:NotificationBanner.kt$@Composable private fun Notification(notificationBannerData: NotificationData)
- MagicNumber:CustomTextField.kt$100
- MagicNumber:LoginScreen.kt$3f
- MagicNumber:RedeemVoucherDialog.kt$30
- MagicNumber:RedeemVoucherDialog.kt$59
- MagicNumber:ResourcesExtensions.kt$3
- NestedBlockDepth:MockApiDispatcher.kt$MockApiDispatcher$override fun dispatch(request: RecordedRequest): MockResponse
- PrintStackTrace:Extensions.kt$ex
- ReturnCount:RelayNameComparator.kt$RelayNameComparator$private infix fun List<String>.compareWith(other: List<String>): Int
- ReturnCount:TalpidVpnService.kt$TalpidVpnService$private fun createTun(config: TunConfig): CreateTunResult
- TooManyFunctions:EditApiAccessMethodViewModel.kt$EditApiAccessMethodViewModel : ViewModel
- TooManyFunctions:SelectLocationViewModel.kt$SelectLocationViewModel : ViewModel
- TooManyFunctions:VpnSettingsViewModel.kt$VpnSettingsViewModel : ViewModel
- UnusedParameter:SimpleMullvadHttpClient.kt$SimpleMullvadHttpClient$body: JSONArray? = null
- UnusedPrivateMember:ConnectivityListener.kt$ConnectivityListener$private fun finalize()
-
-
diff --git a/android/lib/build.gradle.kts b/android/lib/build.gradle.kts
deleted file mode 100644
index 8b137891791f..000000000000
--- a/android/lib/build.gradle.kts
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/android/lib/common/src/main/AndroidManifest.xml b/android/lib/common/src/main/AndroidManifest.xml
index acaad2f3b4fc..cc947c567995 100644
--- a/android/lib/common/src/main/AndroidManifest.xml
+++ b/android/lib/common/src/main/AndroidManifest.xml
@@ -1,4 +1 @@
-
-
-
+
diff --git a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparator.kt b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparator.kt
index a1b1d3b09237..4b848dde7b4b 100644
--- a/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparator.kt
+++ b/android/lib/daemon-grpc/src/main/kotlin/net/mullvad/mullvadvpn/lib/daemon/grpc/RelayNameComparator.kt
@@ -6,28 +6,32 @@ internal object RelayNameComparator : Comparator {
override fun compare(o1: RelayItem.Location.Relay, o2: RelayItem.Location.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)
- }
- private infix fun List.compareWith(other: List): Int {
- this.forEachIndexed { index, s ->
- if (other.size <= index) return 1
- val partsCompareResult = compareStringOrInt(other[index], s)
- if (partsCompareResult != 0) return partsCompareResult
- }
- return 0
+ partitions1
+ .zip(partitions2)
+ .map { (p1, p2) -> compareStringOrInt(p1, p2) }
+ .forEach {
+ if (it != 0) {
+ // Parts differed, return compare result
+ return it
+ }
+ }
+ return partitions1.size.compareTo(partitions2.size)
}
- private fun compareStringOrInt(s1: String, s2: String): Int {
- val int1 = s1.toIntOrNull()
- val int2 = s2.toIntOrNull()
- return if (int1 == null || int2 == null || int1 == int2) {
- s2.compareTo(s1)
+ private fun compareStringOrInt(p1: String, p2: String): Int {
+ val int1 = p1.toIntOrNull()
+ val int2 = p2.toIntOrNull()
+ return if (int1 is Int && int2 is Int && int1 != int2) {
+ // If both are Int and not equal (they might have leading zeros) we should compare them
+ // as numbers
+ int1.compareTo(int2)
} else {
- int2.compareTo(int1)
+ p1.compareTo(p2)
}
}
+ // Regexp that splits digit and non digit, e.g se-got-wg-101 would be ["se-got-wg-", "101"] so
+ // that the number later can be sorted, e.g 9 being listed before 10.
private val regex = "(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)".toRegex()
}
diff --git a/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/VoucherConstant.kt b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/VoucherConstant.kt
new file mode 100644
index 000000000000..a27efa6bf3a7
--- /dev/null
+++ b/android/lib/model/src/main/kotlin/net/mullvad/mullvadvpn/lib/model/VoucherConstant.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.lib.model
+
+const val DAYS_PER_VOUCHER_MONTH = 30
diff --git a/android/lib/resource/build.gradle.kts b/android/lib/resource/build.gradle.kts
index c162b25a0c57..fa9cd5d63e9b 100644
--- a/android/lib/resource/build.gradle.kts
+++ b/android/lib/resource/build.gradle.kts
@@ -23,7 +23,6 @@ android {
lint {
lintConfig = file("lint.xml")
- baseline = file("lint-baseline.xml")
abortOnError = true
warningsAsErrors = true
}
diff --git a/android/lib/resource/lint-baseline.xml b/android/lib/resource/lint-baseline.xml
deleted file mode 100644
index d46970e0dbe1..000000000000
--- a/android/lib/resource/lint-baseline.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/android/lib/resource/src/main/res/drawable-xhdpi/banner.png b/android/lib/resource/src/main/res/drawable-xhdpi/banner.png
deleted file mode 100644
index da8eee678dd3..000000000000
Binary files a/android/lib/resource/src/main/res/drawable-xhdpi/banner.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/drawable/ic_banner_foreground.xml b/android/lib/resource/src/main/res/drawable/ic_banner_foreground.xml
new file mode 100644
index 000000000000..0e542f5c0d28
--- /dev/null
+++ b/android/lib/resource/src/main/res/drawable/ic_banner_foreground.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/lib/resource/src/main/res/drawable/icon_android_mono.xml b/android/lib/resource/src/main/res/drawable/icon_android_mono.xml
index 0d0aa38c1255..45e0acfdd572 100644
--- a/android/lib/resource/src/main/res/drawable/icon_android_mono.xml
+++ b/android/lib/resource/src/main/res/drawable/icon_android_mono.xml
@@ -1,9 +1,11 @@
+ android:fillType="evenOdd"
+ tools:ignore="VectorPath" />
diff --git a/android/lib/resource/src/main/res/mipmap-hdpi/ic_launcher.png b/android/lib/resource/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index eb38145c3cd3..000000000000
Binary files a/android/lib/resource/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/mipmap-mdpi/ic_launcher.png b/android/lib/resource/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 13f0df6b3685..000000000000
Binary files a/android/lib/resource/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/lib/resource/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 2534ca44e38e..000000000000
Binary files a/android/lib/resource/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/lib/resource/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index b01a56476548..000000000000
Binary files a/android/lib/resource/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/lib/resource/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 063b31017eea..000000000000
Binary files a/android/lib/resource/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/android/lib/resource/src/main/res/mipmap/ic_banner.xml b/android/lib/resource/src/main/res/mipmap/ic_banner.xml
new file mode 100644
index 000000000000..7a6010ef373f
--- /dev/null
+++ b/android/lib/resource/src/main/res/mipmap/ic_banner.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/android/lib/resource/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/lib/resource/src/main/res/mipmap/ic_launcher.xml
similarity index 100%
rename from android/lib/resource/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to android/lib/resource/src/main/res/mipmap/ic_launcher.xml
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt
index 905f59f313a4..edeec9a6fe9d 100644
--- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/ConnectivityListener.kt
@@ -55,6 +55,10 @@ class ConnectivityListener {
connectivityManager.unregisterNetworkCallback(callback)
}
+ // DROID-1401
+ // This function has never been used and should most likely be merged into unregister(),
+ // along with ensuring that the lifecycle of it is correct.
+ @Suppress("UnusedPrivateMember")
private fun finalize() {
destroySender(senderAddress)
senderAddress = 0L
diff --git a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
index cf8a775e13f6..9470c88318b6 100644
--- a/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
+++ b/android/lib/talpid/src/main/kotlin/net/mullvad/talpid/TalpidVpnService.kt
@@ -74,6 +74,9 @@ open class TalpidVpnService : LifecycleVpnService() {
synchronized(this) { activeTunStatus = null }
}
+ // DROID-1407
+ // Function is to be cleaned up and lint suppression to be removed.
+ @Suppress("ReturnCount")
private fun createTun(config: TunConfig): CreateTunResult {
if (prepare(this) != null) {
// VPN permission wasn't granted
diff --git a/android/test/build.gradle.kts b/android/test/build.gradle.kts
deleted file mode 100644
index 8b137891791f..000000000000
--- a/android/test/build.gradle.kts
+++ /dev/null
@@ -1 +0,0 @@
-
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 5e702a2d806e..b5dcc1d64790 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
@@ -167,7 +167,7 @@ class SimpleMullvadHttpClient(context: Context) {
): JSONArray? {
val future = RequestFuture.newFuture()
val request =
- object : JsonArrayRequest(method, url, null, future, onErrorResponse) {
+ object : JsonArrayRequest(method, url, body, future, onErrorResponse) {
override fun getHeaders(): MutableMap {
val headers = HashMap()
headers.put("Content-Type", "application/json")
diff --git a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/Extensions.kt b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/Extensions.kt
index 356cacb97e9b..ddb28e3937a2 100644
--- a/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/Extensions.kt
+++ b/android/test/mockapi/src/main/kotlin/net/mullvad/mullvadvpn/test/mockapi/Extensions.kt
@@ -14,8 +14,7 @@ fun Buffer.getAccountNumber(): String? {
return try {
JSONObject(readUtf8()).getString("account_number")
} catch (ex: JSONException) {
- Logger.e("Unable to parse account number")
- ex.printStackTrace()
+ Logger.e("Unable to parse account number", ex)
null
}
}
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 a2978808d775..e230152efe2e 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
@@ -32,6 +32,7 @@ class MockApiDispatcher : Dispatcher() {
private var cachedPubKeyFromAppUnderTest: String? = null
+ @Suppress("CyclomaticComplexMethod", "NestedBlockDepth")
override fun dispatch(request: RecordedRequest): MockResponse {
Logger.d("Request: $request (body=${request.body.peek().readUtf8()})")
return when (request.path ?: "") {