Skip to content

Commit 4c23f35

Browse files
committed
Remove the use of intent provider for request vpn permission
1 parent 50c9d86 commit 4c23f35

File tree

8 files changed

+84
-88
lines changed

8 files changed

+84
-88
lines changed

android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/MullvadApp.kt

+35-16
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package net.mullvad.mullvadvpn.compose.screen
22

3+
import android.content.Intent
4+
import androidx.activity.ComponentActivity
35
import androidx.activity.compose.rememberLauncherForActivityResult
46
import androidx.compose.foundation.layout.fillMaxSize
57
import androidx.compose.runtime.Composable
68
import androidx.compose.runtime.DisposableEffect
79
import androidx.compose.runtime.LaunchedEffect
810
import androidx.compose.ui.ExperimentalComposeUiApi
911
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.platform.LocalContext
1013
import androidx.compose.ui.semantics.semantics
1114
import androidx.compose.ui.semantics.testTagsAsResourceId
1215
import androidx.navigation.NavHostController
@@ -21,14 +24,17 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
2124
import com.ramcosta.composedestinations.rememberNavHostEngine
2225
import com.ramcosta.composedestinations.utils.destination
2326
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
27+
import kotlinx.coroutines.channels.awaitClose
28+
import kotlinx.coroutines.flow.callbackFlow
29+
import kotlinx.coroutines.flow.filter
2430
import kotlinx.coroutines.flow.first
2531
import kotlinx.coroutines.flow.map
2632
import net.mullvad.mullvadvpn.compose.util.RequestVpnPermission
27-
import net.mullvad.mullvadvpn.viewmodel.ChangelogViewModel
33+
import net.mullvad.mullvadvpn.lib.common.constant.KEY_REQUEST_VPN_PERMISSION
34+
import net.mullvad.mullvadvpn.util.getActivity
2835
import net.mullvad.mullvadvpn.viewmodel.DaemonScreenEvent
36+
import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel
2937
import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel
30-
import net.mullvad.mullvadvpn.viewmodel.VpnPermissionSideEffect
31-
import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel
3238
import org.koin.androidx.compose.koinViewModel
3339

3440
private val changeLogDestinations = listOf(ConnectDestination, OutOfTimeDestination)
@@ -41,13 +47,27 @@ fun MullvadApp() {
4147
val navigator: DestinationsNavigator = navHostController.rememberDestinationsNavigator()
4248

4349
val serviceVm = koinViewModel<NoDaemonViewModel>()
44-
val permissionVm = koinViewModel<VpnPermissionViewModel>()
50+
val mullvadAppViewModel = koinViewModel<MullvadAppViewModel>()
4551

4652
DisposableEffect(Unit) {
4753
navHostController.addOnDestinationChangedListener(serviceVm)
4854
onDispose { navHostController.removeOnDestinationChangedListener(serviceVm) }
4955
}
5056

57+
// Get intents
58+
val launchVpnPermission =
59+
rememberLauncherForActivityResult(RequestVpnPermission()) { _ ->
60+
mullvadAppViewModel.connect()
61+
}
62+
val context = LocalContext.current
63+
val activity = (context.getActivity() as ComponentActivity)
64+
LaunchedEffect(navHostController) {
65+
activity
66+
.intents()
67+
.filter { it.action == KEY_REQUEST_VPN_PERMISSION }
68+
.collect { launchVpnPermission.launch(Unit) }
69+
}
70+
5171
DestinationsNavHost(
5272
modifier = Modifier.semantics { testTagsAsResourceId = true }.fillMaxSize(),
5373
engine = engine,
@@ -72,9 +92,8 @@ fun MullvadApp() {
7292
}
7393

7494
// Globally show the changelog
75-
val changeLogsViewModel = koinViewModel<ChangelogViewModel>()
7695
LaunchedEffect(Unit) {
77-
changeLogsViewModel.uiSideEffect.collect {
96+
mullvadAppViewModel.uiSideEffect.collect {
7897
// Wait until we are in an acceptable destination
7998
navHostController.currentBackStackEntryFlow
8099
.map { it.destination() }
@@ -83,15 +102,15 @@ fun MullvadApp() {
83102
navigator.navigate(ChangelogDestination(it))
84103
}
85104
}
105+
}
86106

87-
// Ask for VPN Permission
88-
val launchVpnPermission =
89-
rememberLauncherForActivityResult(RequestVpnPermission()) { _ -> permissionVm.connect() }
90-
LaunchedEffect(Unit) {
91-
changeLogsViewModel.uiSideEffect.collect {
92-
if (it is VpnPermissionSideEffect.ShowDialog) {
93-
launchVpnPermission.launch(Unit)
94-
}
95-
}
107+
private fun ComponentActivity.intents() =
108+
callbackFlow<Intent> {
109+
send(intent)
110+
111+
val listener: (Intent) -> Unit = { trySend(it) }
112+
113+
addOnNewIntentListener(listener)
114+
115+
awaitClose { removeOnNewIntentListener(listener) }
96116
}
97-
}

android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import net.mullvad.mullvadvpn.viewmodel.EditCustomListViewModel
6666
import net.mullvad.mullvadvpn.viewmodel.FilterViewModel
6767
import net.mullvad.mullvadvpn.viewmodel.LoginViewModel
6868
import net.mullvad.mullvadvpn.viewmodel.MtuDialogViewModel
69+
import net.mullvad.mullvadvpn.viewmodel.MullvadAppViewModel
6970
import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel
7071
import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel
7172
import net.mullvad.mullvadvpn.viewmodel.PaymentViewModel
@@ -80,7 +81,6 @@ import net.mullvad.mullvadvpn.viewmodel.SplashViewModel
8081
import net.mullvad.mullvadvpn.viewmodel.SplitTunnelingViewModel
8182
import net.mullvad.mullvadvpn.viewmodel.ViewLogsViewModel
8283
import net.mullvad.mullvadvpn.viewmodel.VoucherDialogViewModel
83-
import net.mullvad.mullvadvpn.viewmodel.VpnPermissionViewModel
8484
import net.mullvad.mullvadvpn.viewmodel.VpnSettingsViewModel
8585
import net.mullvad.mullvadvpn.viewmodel.WelcomeViewModel
8686
import org.apache.commons.validator.routines.InetAddressValidator
@@ -162,7 +162,7 @@ val uiModule = module {
162162

163163
// View models
164164
viewModel { AccountViewModel(get(), get(), get(), IS_PLAY_BUILD) }
165-
viewModel { ChangelogViewModel(get(), get(), BuildConfig.ALWAYS_SHOW_CHANGELOG) }
165+
viewModel { MullvadAppViewModel(get(), get(), get(), BuildConfig.ALWAYS_SHOW_CHANGELOG) }
166166
viewModel {
167167
ConnectViewModel(
168168
get(),
@@ -203,12 +203,12 @@ val uiModule = module {
203203
viewModel { DeleteCustomListConfirmationViewModel(get(), get()) }
204204
viewModel { ServerIpOverridesViewModel(get(), get()) }
205205
viewModel { ResetServerIpOverridesConfirmationViewModel(get()) }
206-
viewModel { VpnPermissionViewModel(get(), get()) }
207206
viewModel { ApiAccessListViewModel(get()) }
208207
viewModel { EditApiAccessMethodViewModel(get(), get(), get()) }
209208
viewModel { SaveApiAccessMethodViewModel(get(), get()) }
210209
viewModel { ApiAccessMethodDetailsViewModel(get(), get()) }
211210
viewModel { DeleteApiAccessMethodConfirmationViewModel(get(), get()) }
211+
viewModel { ChangelogViewModel(get(), get()) }
212212

213213
// This view model must be single so we correctly attach lifecycle and share it with activity
214214
single { NoDaemonViewModel(get()) }

android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package net.mullvad.mullvadvpn.ui
22

3-
import android.content.Intent
43
import android.os.Bundle
54
import androidx.activity.ComponentActivity
65
import androidx.activity.compose.setContent
@@ -63,11 +62,11 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent {
6362
}
6463
super.onCreate(savedInstanceState)
6564

66-
// Needs to be before set content since we want to access the intent in compose
65+
setContent { AppTheme { MullvadApp() } }
66+
6767
if (savedInstanceState == null) {
6868
intentProvider.setStartIntent(intent)
6969
}
70-
setContent { AppTheme { MullvadApp() } }
7170

7271
// This is to protect against tapjacking attacks
7372
window.decorView.filterTouchesWhenObscured = true
@@ -98,11 +97,6 @@ class MainActivity : ComponentActivity(), AndroidScopeComponent {
9897
}
9998
}
10099

101-
override fun onNewIntent(intent: Intent) {
102-
super.onNewIntent(intent)
103-
intentProvider.setStartIntent(intent)
104-
}
105-
106100
fun bindService() {
107101
requestNotificationPermissionIfMissing(requestNotificationPermissionLauncher)
108102
serviceConnectionManager.bind()
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,15 @@
11
package net.mullvad.mullvadvpn.viewmodel
22

3-
import android.os.Parcelable
43
import androidx.lifecycle.ViewModel
5-
import androidx.lifecycle.viewModelScope
6-
import kotlinx.coroutines.flow.MutableSharedFlow
7-
import kotlinx.coroutines.flow.SharedFlow
8-
import kotlinx.coroutines.launch
9-
import kotlinx.parcelize.Parcelize
104
import net.mullvad.mullvadvpn.lib.model.BuildVersion
115
import net.mullvad.mullvadvpn.repository.ChangelogRepository
126

137
class ChangelogViewModel(
148
private val changelogRepository: ChangelogRepository,
159
private val buildVersion: BuildVersion,
16-
private val alwaysShowChangelog: Boolean,
1710
) : ViewModel() {
1811

19-
private val _uiSideEffect = MutableSharedFlow<Changelog>(replay = 1, extraBufferCapacity = 1)
20-
val uiSideEffect: SharedFlow<Changelog> = _uiSideEffect
21-
22-
init {
23-
if (shouldShowChangelog()) {
24-
val changelog =
25-
Changelog(buildVersion.name, changelogRepository.getLastVersionChanges())
26-
viewModelScope.launch { _uiSideEffect.emit(changelog) }
27-
}
28-
}
29-
3012
fun markChangelogAsRead() {
3113
changelogRepository.setVersionCodeOfMostRecentChangelogShowed(buildVersion.code)
3214
}
33-
34-
private fun shouldShowChangelog(): Boolean =
35-
alwaysShowChangelog ||
36-
(changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersion.code &&
37-
changelogRepository.getLastVersionChanges().isNotEmpty())
3815
}
39-
40-
@Parcelize data class Changelog(val version: String, val changes: List<String>) : Parcelable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package net.mullvad.mullvadvpn.viewmodel
2+
3+
import android.os.Parcelable
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import kotlinx.coroutines.flow.MutableSharedFlow
7+
import kotlinx.coroutines.flow.SharedFlow
8+
import kotlinx.coroutines.launch
9+
import kotlinx.parcelize.Parcelize
10+
import net.mullvad.mullvadvpn.lib.model.BuildVersion
11+
import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
12+
import net.mullvad.mullvadvpn.repository.ChangelogRepository
13+
14+
class MullvadAppViewModel(
15+
private val changelogRepository: ChangelogRepository,
16+
private val connectionProxy: ConnectionProxy,
17+
private val buildVersion: BuildVersion,
18+
private val alwaysShowChangelog: Boolean,
19+
) : ViewModel() {
20+
21+
private val _uiSideEffect = MutableSharedFlow<Changelog>(replay = 1, extraBufferCapacity = 1)
22+
val uiSideEffect: SharedFlow<Changelog> = _uiSideEffect
23+
24+
init {
25+
if (shouldShowChangelog()) {
26+
val changelog =
27+
Changelog(buildVersion.name, changelogRepository.getLastVersionChanges())
28+
viewModelScope.launch { _uiSideEffect.emit(changelog) }
29+
}
30+
}
31+
32+
fun connect() {
33+
viewModelScope.launch { connectionProxy.connectWithoutPermissionCheck() }
34+
}
35+
36+
private fun shouldShowChangelog(): Boolean =
37+
alwaysShowChangelog ||
38+
(changelogRepository.getVersionCodeOfMostRecentChangelogShowed() < buildVersion.code &&
39+
changelogRepository.getLastVersionChanges().isNotEmpty())
40+
}
41+
42+
@Parcelize data class Changelog(val version: String, val changes: List<String>) : Parcelable

android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/VpnPermissionViewModel.kt

-34
This file was deleted.

0 commit comments

Comments
 (0)