summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAlbin <albin@mullvad.net>2026-04-22 12:51:48 +0200
committerAlbin <albin@mullvad.net>2026-04-22 12:51:48 +0200
commitef6b61a78c2f8461578c9dcef9592b4580eb9ff9 (patch)
tree8e487acfe1b1d309ec587385b05c27e0a41fc19c
parent9e9af2d3e7d0cb497e131e43e16229091ff8cda2 (diff)
parent05e77fabf590e0db31f5992817b942a8ef8485b4 (diff)
downloadmullvadvpn-ef6b61a78c2f8461578c9dcef9592b4580eb9ff9.tar.xz
mullvadvpn-ef6b61a78c2f8461578c9dcef9592b4580eb9ff9.zip
Merge branch 'determine-download-link-based-on-install-source-droid-2623'
-rw-r--r--android/app/build.gradle.kts2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt22
-rw-r--r--android/lib/feature/appinfo/impl/build.gradle.kts1
-rw-r--r--android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt24
-rw-r--r--android/lib/feature/applisting/api/build.gradle.kts3
-rw-r--r--android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/AppListingTarget.kt3
-rw-r--r--android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/ResolveAppListingUseCase.kt5
-rw-r--r--android/lib/feature/applisting/impl/build.gradle.kts8
-rw-r--r--android/lib/feature/applisting/impl/src/main/AndroidManifest.xml4
-rw-r--r--android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/AndroidInstallSourceProvider.kt31
-rw-r--r--android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/InstallSourceProvider.kt5
-rw-r--r--android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImpl.kt27
-rw-r--r--android/lib/feature/applisting/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImplTest.kt79
-rw-r--r--android/lib/feature/home/impl/build.gradle.kts1
-rw-r--r--android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt24
-rw-r--r--android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt5
-rw-r--r--android/settings.gradle.kts2
17 files changed, 201 insertions, 45 deletions
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 023508752f..fa94ff9f0b 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -393,6 +393,8 @@ dependencies {
implementation(projects.lib.feature.appicon.impl)
implementation(projects.lib.feature.appinfo.impl)
implementation(projects.lib.feature.appinfo.api)
+ implementation(projects.lib.feature.applisting.impl)
+ implementation(projects.lib.feature.applisting.api)
implementation(projects.lib.feature.appearance.impl)
implementation(projects.lib.feature.autoconnect.impl)
implementation(projects.lib.feature.customlist.impl)
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 c86532f65f..686e322d74 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
@@ -20,6 +20,10 @@ import net.mullvad.mullvadvpn.feature.apiaccess.impl.screen.save.SaveApiAccessMe
import net.mullvad.mullvadvpn.feature.appicon.impl.AppIconViewModel
import net.mullvad.mullvadvpn.feature.appinfo.impl.AppInfoViewModel
import net.mullvad.mullvadvpn.feature.appinfo.impl.changelog.ChangelogViewModel
+import net.mullvad.mullvadvpn.feature.applisting.api.ResolveAppListingUseCase
+import net.mullvad.mullvadvpn.feature.applisting.impl.AndroidInstallSourceProvider
+import net.mullvad.mullvadvpn.feature.applisting.impl.InstallSourceProvider
+import net.mullvad.mullvadvpn.feature.applisting.impl.ResolveAppListingUseCaseImpl
import net.mullvad.mullvadvpn.feature.autoconnect.impl.AutoConnectAndLockdownModeViewModel
import net.mullvad.mullvadvpn.feature.customlist.impl.screen.create.CreateCustomListDialogViewModel
import net.mullvad.mullvadvpn.feature.customlist.impl.screen.delete.DeleteCustomListConfirmationViewModel
@@ -129,7 +133,15 @@ val uiModule = module {
ComponentName(androidContext(), AutoStartVpnBootCompletedReceiver::class.java)
}
- single { PackageName(androidContext().packageName) }
+ single<InstallSourceProvider> { AndroidInstallSourceProvider(androidContext()) }
+ single<ResolveAppListingUseCase> {
+ ResolveAppListingUseCaseImpl(
+ resources = androidContext().resources,
+ packageName = PackageName(androidContext().packageName),
+ isPlayBuild = IS_PLAY_BUILD,
+ installSourceProvider = get(),
+ )
+ }
single { ApplicationsProvider(get(), get()) }
scope<MainActivity> { scoped { ServiceConnectionManager(androidContext()) } }
single { InetAddressValidator.getInstance() }
@@ -263,10 +275,8 @@ val uiModule = module {
viewModel {
AppInfoViewModel(
appVersionInfoRepository = get(),
- resources = get(),
isPlayBuild = IS_PLAY_BUILD,
- isFdroidBuild = false,
- self = get(),
+ resolveAppListing = get(),
)
}
viewModel {
@@ -283,10 +293,8 @@ val uiModule = module {
connectionProxy = get(),
lastKnownLocationUseCase = get(),
systemVpnSettingsUseCase = get(),
- resources = get(),
isPlayBuild = IS_PLAY_BUILD,
- isFdroidBuild = false,
- self = get(),
+ resolveAppListing = get(),
)
}
viewModel { params -> DeviceListViewModel(accountNumber = params.get(), get()) }
diff --git a/android/lib/feature/appinfo/impl/build.gradle.kts b/android/lib/feature/appinfo/impl/build.gradle.kts
index ea0ec035e7..e408702233 100644
--- a/android/lib/feature/appinfo/impl/build.gradle.kts
+++ b/android/lib/feature/appinfo/impl/build.gradle.kts
@@ -10,6 +10,7 @@ android { namespace = "net.mullvad.mullvadvpn.feature.appinfo.impl" }
dependencies {
implementation(projects.lib.feature.appinfo.api)
+ implementation(projects.lib.feature.applisting.api)
implementation(projects.lib.repository)
implementation(libs.koin.compose)
diff --git a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
index 7c39a1d9f8..86c01717d0 100644
--- a/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
+++ b/android/lib/feature/appinfo/impl/src/main/java/net/mullvad/mullvadvpn/feature/appinfo/impl/AppInfoViewModel.kt
@@ -1,6 +1,5 @@
package net.mullvad.mullvadvpn.feature.appinfo.impl
-import android.content.res.Resources
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@@ -12,18 +11,15 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.feature.applisting.api.ResolveAppListingUseCase
import net.mullvad.mullvadvpn.lib.common.Lc
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
-import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.repository.AppVersionInfoRepository
-import net.mullvad.mullvadvpn.lib.ui.resource.R
class AppInfoViewModel(
appVersionInfoRepository: AppVersionInfoRepository,
- private val resources: Resources,
private val isPlayBuild: Boolean,
- private val isFdroidBuild: Boolean,
- private val self: PackageName,
+ private val resolveAppListing: ResolveAppListingUseCase,
) : ViewModel() {
private val _uiSideEffect = Channel<AppInfoSideEffect>()
@@ -39,18 +35,12 @@ class AppInfoViewModel(
)
fun openAppListing() = viewModelScope.launch {
+ val target = resolveAppListing()
val sideEffect =
- if (isPlayBuild || isFdroidBuild) {
- AppInfoSideEffect.OpenUri(
- uri = resources.getString(R.string.market_uri, self.value).toUri(),
- errorMessage = resources.getString(R.string.uri_market_app_not_found),
- )
- } else {
- AppInfoSideEffect.OpenUri(
- uri = resources.getString(R.string.download_url).toUri(),
- errorMessage = resources.getString(R.string.uri_browser_app_not_found),
- )
- }
+ AppInfoSideEffect.OpenUri(
+ uri = target.listingUri.toUri(),
+ errorMessage = target.errorMessage,
+ )
_uiSideEffect.send(sideEffect)
}
}
diff --git a/android/lib/feature/applisting/api/build.gradle.kts b/android/lib/feature/applisting/api/build.gradle.kts
new file mode 100644
index 0000000000..c98931c67f
--- /dev/null
+++ b/android/lib/feature/applisting/api/build.gradle.kts
@@ -0,0 +1,3 @@
+plugins { alias(libs.plugins.mullvad.android.library.feature.api) }
+
+android { namespace = "net.mullvad.mullvadvpn.feature.applisting.api" }
diff --git a/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/AppListingTarget.kt b/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/AppListingTarget.kt
new file mode 100644
index 0000000000..2275539ebf
--- /dev/null
+++ b/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/AppListingTarget.kt
@@ -0,0 +1,3 @@
+package net.mullvad.mullvadvpn.feature.applisting.api
+
+data class AppListingTarget(val listingUri: String, val errorMessage: String)
diff --git a/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/ResolveAppListingUseCase.kt b/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/ResolveAppListingUseCase.kt
new file mode 100644
index 0000000000..0766b4f5c7
--- /dev/null
+++ b/android/lib/feature/applisting/api/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/api/ResolveAppListingUseCase.kt
@@ -0,0 +1,5 @@
+package net.mullvad.mullvadvpn.feature.applisting.api
+
+fun interface ResolveAppListingUseCase {
+ operator fun invoke(): AppListingTarget
+}
diff --git a/android/lib/feature/applisting/impl/build.gradle.kts b/android/lib/feature/applisting/impl/build.gradle.kts
new file mode 100644
index 0000000000..152046e92b
--- /dev/null
+++ b/android/lib/feature/applisting/impl/build.gradle.kts
@@ -0,0 +1,8 @@
+plugins {
+ alias(libs.plugins.mullvad.android.library)
+ alias(libs.plugins.mullvad.android.library.feature.impl)
+}
+
+android { namespace = "net.mullvad.mullvadvpn.feature.applisting.impl" }
+
+dependencies { implementation(projects.lib.feature.applisting.api) }
diff --git a/android/lib/feature/applisting/impl/src/main/AndroidManifest.xml b/android/lib/feature/applisting/impl/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..8bdb7e14b3
--- /dev/null
+++ b/android/lib/feature/applisting/impl/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
diff --git a/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/AndroidInstallSourceProvider.kt b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/AndroidInstallSourceProvider.kt
new file mode 100644
index 0000000000..0e48140df9
--- /dev/null
+++ b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/AndroidInstallSourceProvider.kt
@@ -0,0 +1,31 @@
+package net.mullvad.mullvadvpn.feature.applisting.impl
+
+import android.content.Context
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import android.os.Build
+
+class AndroidInstallSourceProvider(private val context: Context) : InstallSourceProvider {
+ override fun isInstalledFromStore(): Boolean {
+ val packageName = context.packageName
+ val packageManager = context.packageManager
+ return when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ->
+ try {
+ packageManager.getInstallSourceInfo(packageName).packageSource ==
+ PackageInstaller.PACKAGE_SOURCE_STORE
+ } catch (_: PackageManager.NameNotFoundException) {
+ false
+ }
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->
+ try {
+ packageManager.getInstallSourceInfo(packageName).installingPackageName != null
+ } catch (_: PackageManager.NameNotFoundException) {
+ false
+ }
+ else ->
+ @Suppress("DEPRECATION")
+ (packageManager.getInstallerPackageName(packageName) != null)
+ }
+ }
+}
diff --git a/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/InstallSourceProvider.kt b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/InstallSourceProvider.kt
new file mode 100644
index 0000000000..c1b8910c74
--- /dev/null
+++ b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/InstallSourceProvider.kt
@@ -0,0 +1,5 @@
+package net.mullvad.mullvadvpn.feature.applisting.impl
+
+fun interface InstallSourceProvider {
+ fun isInstalledFromStore(): Boolean
+}
diff --git a/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImpl.kt b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImpl.kt
new file mode 100644
index 0000000000..0fa6058407
--- /dev/null
+++ b/android/lib/feature/applisting/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImpl.kt
@@ -0,0 +1,27 @@
+package net.mullvad.mullvadvpn.feature.applisting.impl
+
+import android.content.res.Resources
+import net.mullvad.mullvadvpn.feature.applisting.api.AppListingTarget
+import net.mullvad.mullvadvpn.feature.applisting.api.ResolveAppListingUseCase
+import net.mullvad.mullvadvpn.lib.model.PackageName
+import net.mullvad.mullvadvpn.lib.ui.resource.R
+
+class ResolveAppListingUseCaseImpl(
+ private val resources: Resources,
+ private val packageName: PackageName,
+ private val isPlayBuild: Boolean,
+ private val installSourceProvider: InstallSourceProvider,
+) : ResolveAppListingUseCase {
+ override fun invoke(): AppListingTarget =
+ if (isPlayBuild || installSourceProvider.isInstalledFromStore()) {
+ AppListingTarget(
+ listingUri = resources.getString(R.string.market_uri, packageName.value),
+ errorMessage = resources.getString(R.string.uri_market_app_not_found),
+ )
+ } else {
+ AppListingTarget(
+ listingUri = resources.getString(R.string.download_url),
+ errorMessage = resources.getString(R.string.uri_browser_app_not_found),
+ )
+ }
+}
diff --git a/android/lib/feature/applisting/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImplTest.kt b/android/lib/feature/applisting/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImplTest.kt
new file mode 100644
index 0000000000..495b30db51
--- /dev/null
+++ b/android/lib/feature/applisting/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/applisting/impl/ResolveAppListingUseCaseImplTest.kt
@@ -0,0 +1,79 @@
+package net.mullvad.mullvadvpn.feature.applisting.impl
+
+import android.content.res.Resources
+import io.mockk.every
+import io.mockk.mockk
+import kotlin.test.assertEquals
+import net.mullvad.mullvadvpn.lib.model.PackageName
+import net.mullvad.mullvadvpn.lib.ui.resource.R
+import org.junit.jupiter.api.Test
+
+class ResolveAppListingUseCaseImplTest {
+
+ private val mockResources: Resources = mockk()
+
+ @Test
+ fun `when play build should return market uri`() {
+ // Arrange
+ val useCase = createUseCase(isPlayBuild = true, isStoreInstall = false)
+
+ // Act
+ val result = useCase()
+
+ // Assert
+ assertEquals(MARKET_URI, result.listingUri)
+ assertEquals(MARKET_ERROR, result.errorMessage)
+ }
+
+ @Test
+ fun `when store install should return market uri`() {
+ // Arrange
+ val useCase = createUseCase(isPlayBuild = false, isStoreInstall = true)
+
+ // Act
+ val result = useCase()
+
+ // Assert
+ assertEquals(MARKET_URI, result.listingUri)
+ assertEquals(MARKET_ERROR, result.errorMessage)
+ }
+
+ @Test
+ fun `when sideloaded build should return download url`() {
+ // Arrange
+ val useCase = createUseCase(isPlayBuild = false, isStoreInstall = false)
+
+ // Act
+ val result = useCase()
+
+ // Assert
+ assertEquals(DOWNLOAD_URL, result.listingUri)
+ assertEquals(BROWSER_ERROR, result.errorMessage)
+ }
+
+ private fun createUseCase(
+ isPlayBuild: Boolean,
+ isStoreInstall: Boolean,
+ ): ResolveAppListingUseCaseImpl {
+ every { mockResources.getString(R.string.market_uri, PACKAGE_NAME.value) } returns
+ MARKET_URI
+ every { mockResources.getString(R.string.download_url) } returns DOWNLOAD_URL
+ every { mockResources.getString(R.string.uri_market_app_not_found) } returns MARKET_ERROR
+ every { mockResources.getString(R.string.uri_browser_app_not_found) } returns BROWSER_ERROR
+
+ return ResolveAppListingUseCaseImpl(
+ resources = mockResources,
+ packageName = PACKAGE_NAME,
+ isPlayBuild = isPlayBuild,
+ installSourceProvider = InstallSourceProvider { isStoreInstall },
+ )
+ }
+
+ companion object {
+ private val PACKAGE_NAME = PackageName("net.mullvad.mullvadvpn")
+ private const val MARKET_URI = "market://details?id=net.mullvad.mullvadvpn"
+ private const val DOWNLOAD_URL = "https://mullvad.net/download/vpn/android"
+ private const val MARKET_ERROR = "No Android app store installed, could not open link"
+ private const val BROWSER_ERROR = "No browser app installed, could not open link"
+ }
+}
diff --git a/android/lib/feature/home/impl/build.gradle.kts b/android/lib/feature/home/impl/build.gradle.kts
index ee7b90f085..7b27e30b81 100644
--- a/android/lib/feature/home/impl/build.gradle.kts
+++ b/android/lib/feature/home/impl/build.gradle.kts
@@ -15,6 +15,7 @@ dependencies {
implementation(projects.lib.feature.addtime.impl)
implementation(projects.lib.feature.anticensorship.api)
implementation(projects.lib.feature.appinfo.api)
+ implementation(projects.lib.feature.applisting.api)
implementation(projects.lib.feature.daita.api)
implementation(projects.lib.feature.home.api)
implementation(projects.lib.feature.location.api)
diff --git a/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
index eae192ccad..18efcb3637 100644
--- a/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
+++ b/android/lib/feature/home/impl/src/main/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModel.kt
@@ -1,6 +1,5 @@
package net.mullvad.mullvadvpn.feature.home.impl.connect
-import android.content.res.Resources
import android.net.Uri
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
@@ -18,6 +17,7 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
+import net.mullvad.mullvadvpn.feature.applisting.api.ResolveAppListingUseCase
import net.mullvad.mullvadvpn.feature.home.impl.connect.notificationbanner.InAppNotificationController
import net.mullvad.mullvadvpn.lib.common.constant.VIEW_MODEL_STOP_TIMEOUT
import net.mullvad.mullvadvpn.lib.common.util.combine
@@ -27,7 +27,6 @@ import net.mullvad.mullvadvpn.lib.model.ActionAfterDisconnect
import net.mullvad.mullvadvpn.lib.model.ConnectError
import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.model.DisconnectReason
-import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.PrepareError
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
@@ -39,7 +38,6 @@ import net.mullvad.mullvadvpn.lib.repository.DeviceRepository
import net.mullvad.mullvadvpn.lib.repository.NewDeviceRepository
import net.mullvad.mullvadvpn.lib.repository.PaymentLogic
import net.mullvad.mullvadvpn.lib.repository.UserPreferencesRepository
-import net.mullvad.mullvadvpn.lib.ui.resource.R
import net.mullvad.mullvadvpn.lib.usecase.LastKnownLocationUseCase
import net.mullvad.mullvadvpn.lib.usecase.OutOfTimeUseCase
import net.mullvad.mullvadvpn.lib.usecase.SelectedLocationTitleUseCase
@@ -59,10 +57,8 @@ class ConnectViewModel(
private val connectionProxy: ConnectionProxy,
lastKnownLocationUseCase: LastKnownLocationUseCase,
private val systemVpnSettingsUseCase: SystemVpnSettingsAvailableUseCase,
- private val resources: Resources,
private val isPlayBuild: Boolean,
- private val isFdroidBuild: Boolean,
- private val self: PackageName,
+ private val resolveAppListing: ResolveAppListingUseCase,
) : ViewModel() {
private val _uiSideEffect = Channel<UiSideEffect>()
@@ -198,18 +194,12 @@ class ConnectViewModel(
}
fun openAppListing() = viewModelScope.launch {
+ val target = resolveAppListing()
val sideEffect =
- if (isPlayBuild || isFdroidBuild) {
- UiSideEffect.OpenUri(
- uri = resources.getString(R.string.market_uri, self.value).toUri(),
- errorMessage = resources.getString(R.string.uri_market_app_not_found),
- )
- } else {
- UiSideEffect.OpenUri(
- uri = resources.getString(R.string.download_url).toUri(),
- errorMessage = resources.getString(R.string.uri_browser_app_not_found),
- )
- }
+ UiSideEffect.OpenUri(
+ uri = target.listingUri.toUri(),
+ errorMessage = target.errorMessage,
+ )
_uiSideEffect.send(sideEffect)
}
diff --git a/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
index 141d2e6695..74291039de 100644
--- a/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
+++ b/android/lib/feature/home/impl/src/test/kotlin/net/mullvad/mullvadvpn/feature/home/impl/connect/ConnectViewModelTest.kt
@@ -26,7 +26,6 @@ import net.mullvad.mullvadvpn.lib.model.DisconnectReason
import net.mullvad.mullvadvpn.lib.model.ErrorState
import net.mullvad.mullvadvpn.lib.model.GeoIpLocation
import net.mullvad.mullvadvpn.lib.model.InAppNotification
-import net.mullvad.mullvadvpn.lib.model.PackageName
import net.mullvad.mullvadvpn.lib.model.TunnelEndpoint
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.model.WebsiteAuthToken
@@ -124,10 +123,8 @@ class ConnectViewModelTest {
connectionProxy = mockConnectionProxy,
lastKnownLocationUseCase = mockLastKnownLocationUseCase,
systemVpnSettingsUseCase = mockSystemVpnSettingsUseCase,
- resources = mockk(),
isPlayBuild = false,
- isFdroidBuild = false,
- self = PackageName("net.mullvad.mullvadvpn"),
+ resolveAppListing = mockk(),
)
}
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 06c92449e9..95b89e9b52 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -46,6 +46,8 @@ include(
":lib:feature:appicon:api",
":lib:feature:appinfo:impl",
":lib:feature:appinfo:api",
+ ":lib:feature:applisting:impl",
+ ":lib:feature:applisting:api",
":lib:feature:appearance:impl",
":lib:feature:appearance:api",
":lib:feature:autoconnect:impl",