diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
index 0fb165ea89b9..538f9cc49fad 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt
@@ -1,5 +1,7 @@
 package net.mullvad.mullvadvpn.compose.screen
 
+import android.content.Intent
+import androidx.activity.ComponentActivity
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
@@ -7,6 +9,7 @@ import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.testTagsAsResourceId
 import androidx.navigation.NavHostController
@@ -21,14 +24,17 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
 import com.ramcosta.composedestinations.rememberNavHostEngine
 import com.ramcosta.composedestinations.utils.destination
 import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import net.mullvad.mullvadvpn.compose.util.RequestVpnPermission
-import net.mullvad.mullvadvpn.viewmodel.ChangelogViewModel
+import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PERMISSION
+import net.mullvad.mullvadvpn.util.getActivity
 import net.mullvad.mullvadvpn.viewmodel.DaemonScreenEvent
+import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel
 import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel
-import net.mullvad.mullvadvpn.viewmodel.VpnPermissionSideEffect
-import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel
 import org.koin.androidx.compose.koinViewModel
 
 private val changeLogDestinations = listOf(ConnectDestination, OutOfTimeDestination)
@@ -41,13 +47,27 @@ fun MullvadApp() {
     val navigator: DestinationsNavigator = navHostController.rememberDestinationsNavigator()
 
     val serviceVm = koinViewModel<NoDaemonViewModel>()
-    val permissionVm = koinViewModel<VpnPermissionViewModel>()
+    val mullvadAppViewModel = koinViewModel<MullvadAppViewModel>()
 
     DisposableEffect(Unit) {
         navHostController.addOnDestinationChangedListener(serviceVm)
         onDispose { navHostController.removeOnDestinationChangedListener(serviceVm) }
     }
 
+    // Get intents
+    val launchVpnPermission =
+        rememberLauncherForActivityResult(RequestVpnPermission()) { _ ->
+            mullvadAppViewModel.connect()
+        }
+    val context = LocalContext.current
+    val activity = (context.getActivity() as ComponentActivity)
+    LaunchedEffect(navHostController) {
+        activity
+            .intents()
+            .filter { it.action == KEY_REQUEST_VPN_PERMISSION }
+            .collect { launchVpnPermission.launch(Unit) }
+    }
+
     DestinationsNavHost(
         modifier = Modifier.semantics { testTagsAsResourceId = true }.fillMaxSize(),
         engine = engine,
@@ -72,9 +92,8 @@ fun MullvadApp() {
     }
 
     // Globally show the changelog
-    val changeLogsViewModel = koinViewModel<ChangelogViewModel>()
     LaunchedEffect(Unit) {
-        changeLogsViewModel.uiSideEffect.collect {
+        mullvadAppViewModel.uiSideEffect.collect {
             // Wait until we are in an acceptable destination
             navHostController.currentBackStackEntryFlow
                 .map { it.destination() }
@@ -83,15 +102,15 @@ fun MullvadApp() {
             navigator.navigate(ChangelogDestination(it))
         }
     }
+}
 
-    // Ask for VPN Permission
-    val launchVpnPermission =
-        rememberLauncherForActivityResult(RequestVpnPermission()) { _ -> permissionVm.connect() }
-    LaunchedEffect(Unit) {
-        changeLogsViewModel.uiSideEffect.collect {
-            if (it is VpnPermissionSideEffect.ShowDialog) {
-                launchVpnPermission.launch(Unit)
-            }
-        }
+private fun ComponentActivity.intents() =
+    callbackFlow<Intent> {
+        send(intent)
+
+        val listener: (Intent) -> Unit = { trySend(it) }
+
+        addOnNewIntentListener(listener)
+
+        awaitClose { removeOnNewIntentListener(listener) }
     }
-}
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 6b909d394d40..af314345fd4c 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
@@ -66,6 +66,7 @@ import net.mullvad.mullvadvpn.viewmodel.EditCustomListViewModel
 import net.mullvad.mullvadvpn.viewmodel.FilterViewModel
 import net.mullvad.mullvadvpn.viewmodel.LoginViewModel
 import net.mullvad.mullvadvpn.viewmodel.MtuDialogViewModel
+import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel
 import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel
 import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel
 import net.mullvad.mullvadvpn.viewmodel.PaymentViewModel
@@ -80,7 +81,6 @@ import net.mullvad.mullvadvpn.viewmodel.SplashViewModel
 import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel
 import net.mullvad.mullvadvpn.viewmodel.ViewLogsViewModel
 import net.mullvad.mullvadvpn.viewmodel.VoucherDialogViewModel
-import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel
 import net.mullvad.mullvadvpn.viewmodel.VpnSettingsViewModel
 import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel
 import org.apache.commons.validator.routines.InetAddressValidator
@@ -162,7 +162,7 @@ val uiModule = module {
 
     // View models
     viewModel { AccountViewModel(get(), get(), get(), IS_PLAY_BUILD) }
-    viewModel { ChangelogViewModel(get(), get(), BuildConfig.ALWAYS_SHOW_CHANGELOG) }
+    viewModel { MullvadAppViewModel(get(), get(), get(), BuildConfig.ALWAYS_SHOW_CHANGELOG) }
     viewModel {
         ConnectViewModel(
             get(),
@@ -205,12 +205,12 @@ val uiModule = module {
     viewModel { DeleteCustomListConfirmationViewModel(get(), get()) }
     viewModel { ServerIpOverridesViewModel(get(), get()) }
     viewModel { ResetServerIpOverridesConfirmationViewModel(get()) }
-    viewModel { VpnPermissionViewModel(get(), get()) }
     viewModel { ApiAccessListViewModel(get()) }
     viewModel { EditApiAccessMethodViewModel(get(), get(), get()) }
     viewModel { SaveApiAccessMethodViewModel(get(), get()) }
     viewModel { ApiAccessMethodDetailsViewModel(get(), get()) }
     viewModel { DeleteApiAccessMethodConfirmationViewModel(get(), get()) }
+    viewModel { ChangelogViewModel(get(), get()) }
 
     // This view model must be single so we correctly attach lifecycle and share it with activity
     single { NoDaemonViewModel(get()) }
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 a4ae13113902..c3cd5a20dfdd 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
@@ -1,6 +1,5 @@
 package net.mullvad.mullvadvpn.ui
 
-import android.content.Intent
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
@@ -63,11 +62,11 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent {
         }
         super.onCreate(savedInstanceState)
 
-        // Needs to be before set content since we want to access the intent in compose
+        setContent { AppTheme { MullvadApp() } }
+
         if (savedInstanceState == null) {
             intentProvider.setStartIntent(intent)
         }
-        setContent { AppTheme { MullvadApp() } }
 
         // This is to protect against tapjacking attacks
         window.decorView.filterTouchesWhenObscured = true
@@ -98,11 +97,6 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent {
         }
     }
 
-    override fun onNewIntent(intent: Intent) {
-        super.onNewIntent(intent)
-        intentProvider.setStartIntent(intent)
-    }
-
     fun bindService() {
         requestNotificationPermissionIfMissing(requestNotificationPermissionLauncher)
         serviceConnectionManager.bind()
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 f0817ea4feb9..b0001a164aca 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
@@ -1,40 +1,15 @@
 package net.mullvad.mullvadvpn.viewmodel
 
-import android.os.Parcelable
 import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.launch
-import kotlinx.parcelize.Parcelize
 import net.mullvad.mullvadvpn.lib.model.BuildVersion
 import net.mullvad.mullvadvpn.repository.ChangelogRepository
 
 class ChangelogViewModel(
     private val changelogRepository: ChangelogRepository,
     private val buildVersion: BuildVersion,
-    private val alwaysShowChangelog: Boolean,
 ) : ViewModel() {
 
-    private val _uiSideEffect = MutableSharedFlow<Changelog>(replay = 1, extraBufferCapacity = 1)
-    val uiSideEffect: SharedFlow<Changelog> = _uiSideEffect
-
-    init {
-        if (shouldShowChangelog()) {
-            val changelog =
-                Changelog(buildVersion.name, changelogRepository.getLastVersionChanges())
-            viewModelScope.launch { _uiSideEffect.emit(changelog) }
-        }
-    }
-
     fun markChangelogAsRead() {
         changelogRepository.setVersionCodeOfMostRecentChangelogShowed(buildVersion.code)
     }
-
-    private fun shouldShowChangelog(): Boolean =
-        alwaysShowChangelog ||
-            (changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersion.code &&
-                changelogRepository.getLastVersionChanges().isNotEmpty())
 }
-
-@Parcelize data class Changelog(val version: String, val changes: List<String>) : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModel.kt
new file mode 100644
index 000000000000..88dd7f1a46ae
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModel.kt
@@ -0,0 +1,42 @@
+package net.mullvad.mullvadvpn.viewmodel
+
+import android.os.Parcelable
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.launch
+import kotlinx.parcelize.Parcelize
+import net.mullvad.mullvadvpn.lib.model.BuildVersion
+import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
+import net.mullvad.mullvadvpn.repository.ChangelogRepository
+
+class MullvadAppViewModel(
+    private val changelogRepository: ChangelogRepository,
+    private val connectionProxy: ConnectionProxy,
+    private val buildVersion: BuildVersion,
+    private val alwaysShowChangelog: Boolean,
+) : ViewModel() {
+
+    private val _uiSideEffect = MutableSharedFlow<Changelog>(replay = 1, extraBufferCapacity = 1)
+    val uiSideEffect: SharedFlow<Changelog> = _uiSideEffect
+
+    init {
+        if (shouldShowChangelog()) {
+            val changelog =
+                Changelog(buildVersion.name, changelogRepository.getLastVersionChanges())
+            viewModelScope.launch { _uiSideEffect.emit(changelog) }
+        }
+    }
+
+    fun connect() {
+        viewModelScope.launch { connectionProxy.connectWithoutPermissionCheck() }
+    }
+
+    private fun shouldShowChangelog(): Boolean =
+        alwaysShowChangelog ||
+            (changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersion.code &&
+                changelogRepository.getLastVersionChanges().isNotEmpty())
+}
+
+@Parcelize data class Changelog(val version: String, val changes: List<String>) : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt
deleted file mode 100644
index 1e5972b53897..000000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package net.mullvad.mullvadvpn.viewmodel
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.launch
-import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PERMISSION
-import net.mullvad.mullvadvpn.lib.intent.IntentProvider
-import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
-
-class VpnPermissionViewModel(
-    intentProvider: IntentProvider,
-    private val connectionProxy: ConnectionProxy,
-) : ViewModel() {
-    val uiSideEffect: Flow<VpnPermissionSideEffect> =
-        intentProvider.intents
-            .filter { it?.action == KEY_REQUEST_VPN_PERMISSION }
-            .distinctUntilChanged()
-            .map { VpnPermissionSideEffect.ShowDialog }
-            .shareIn(viewModelScope, SharingStarted.WhileSubscribed())
-
-    fun connect() {
-        viewModelScope.launch { connectionProxy.connectWithoutPermissionCheck() }
-    }
-}
-
-sealed interface VpnPermissionSideEffect {
-    data object ShowDialog : VpnPermissionSideEffect
-}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModelTest.kt
similarity index 81%
rename from android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt
rename to android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModelTest.kt
index 7888f02a4d18..8e5af38a7c16 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/ChangelogViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/MullvadAppViewModelTest.kt
@@ -11,6 +11,7 @@ import kotlin.test.assertEquals
 import kotlinx.coroutines.test.runTest
 import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
 import net.mullvad.mullvadvpn.lib.model.BuildVersion
+import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
 import net.mullvad.mullvadvpn.repository.ChangelogRepository
 import org.junit.jupiter.api.AfterEach
 import org.junit.jupiter.api.BeforeEach
@@ -18,11 +19,12 @@ import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 
 @ExtendWith(TestCoroutineRule::class)
-class ChangelogViewModelTest {
+class MullvadAppViewModelTest {
 
     @MockK private lateinit var mockedChangelogRepository: ChangelogRepository
+    @MockK private lateinit var connectionProxy: ConnectionProxy
 
-    private lateinit var viewModel: ChangelogViewModel
+    private lateinit var viewModel: MullvadAppViewModel
 
     private val buildVersion = BuildVersion("1.0", 10)
 
@@ -43,7 +45,8 @@ class ChangelogViewModelTest {
         // Arrange
         every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns
             buildVersion.code
-        viewModel = ChangelogViewModel(mockedChangelogRepository, buildVersion, false)
+        viewModel =
+            MullvadAppViewModel(mockedChangelogRepository, connectionProxy, buildVersion, false)
 
         // If we have the most up to date version code, we should not show the changelog dialog
         viewModel.uiSideEffect.test { expectNoEvents() }
@@ -58,7 +61,8 @@ class ChangelogViewModelTest {
             version
         every { mockedChangelogRepository.getLastVersionChanges() } returns changes
 
-        viewModel = ChangelogViewModel(mockedChangelogRepository, buildVersion, false)
+        viewModel =
+            MullvadAppViewModel(mockedChangelogRepository, connectionProxy, buildVersion, false)
         // Given a new version with a change log we should return it
         viewModel.uiSideEffect.test {
             assertEquals(awaitItem(), Changelog(version = buildVersion.name, changes = changes))
@@ -71,7 +75,8 @@ class ChangelogViewModelTest {
         every { mockedChangelogRepository.getVersionCodeOfMostRecentChangelogShowed() } returns -1
         every { mockedChangelogRepository.getLastVersionChanges() } returns emptyList()
 
-        viewModel = ChangelogViewModel(mockedChangelogRepository, buildVersion, false)
+        viewModel =
+            MullvadAppViewModel(mockedChangelogRepository, connectionProxy, buildVersion, false)
         // Given a new version with a change log we should not return it
         viewModel.uiSideEffect.test { expectNoEvents() }
     }