mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
fix(android): fix view state lifecycle around tunnel/auth (#9621)
`onViewCreated()` is called when the view initializes, and then `onResume()` is called right after, in addition to anytime the view is shown again. To prevent showing the VPN permission activity twice, we remove the `checkTunnelState()` from onViewCreated, allowing only `onResume()` to call it. A boolean flag is added to track whether this is the "first" launch of the app in order to determine whether to `connectOnStart`. Fixes #9584 --------- Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
This commit is contained in:
@@ -29,15 +29,11 @@ internal class AuthViewModel
|
||||
repo.saveNonceSync(nonce)
|
||||
repo.saveStateSync(state)
|
||||
val config = repo.getConfigSync()
|
||||
val token = repo.getTokenSync()
|
||||
|
||||
actionMutableLiveData.postValue(
|
||||
if (authFlowLaunched || token != null) {
|
||||
ViewAction.NavigateToSignIn
|
||||
} else {
|
||||
authFlowLaunched = true
|
||||
ViewAction.LaunchAuthFlow("${config.authUrl}/${config.accountSlug}?state=$state&nonce=$nonce&as=client")
|
||||
},
|
||||
ViewAction.LaunchAuthFlow(
|
||||
"${config.authUrl}/${config.accountSlug}?state=$state&nonce=$nonce&as=client",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import dev.firezone.android.features.session.ui.SessionActivity
|
||||
internal class SplashFragment : Fragment(R.layout.fragment_splash) {
|
||||
private lateinit var binding: FragmentSplashBinding
|
||||
private val viewModel: SplashViewModel by viewModels()
|
||||
private var isInitialLaunch: Boolean = true
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
@@ -26,15 +27,23 @@ internal class SplashFragment : Fragment(R.layout.fragment_splash) {
|
||||
|
||||
setupActionObservers()
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// Trigger the initial check for tunnel state
|
||||
viewModel.checkTunnelState(requireContext(), isInitialLaunch = true)
|
||||
savedInstanceState?.let {
|
||||
isInitialLaunch = it.getBoolean("isInitialLaunch", true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
// Save the initial launch state to handle edge cases where the fragment is recreated
|
||||
outState.putBoolean("isInitialLaunch", isInitialLaunch)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.checkTunnelState(requireContext(), isInitialLaunch = false)
|
||||
viewModel.checkTunnelState(requireContext(), isInitialLaunch)
|
||||
|
||||
isInitialLaunch = false
|
||||
}
|
||||
|
||||
private fun setupActionObservers() {
|
||||
|
||||
@@ -28,11 +28,6 @@ internal class SplashViewModel
|
||||
private val actionMutableLiveData = MutableLiveData<ViewAction>()
|
||||
val actionLiveData: LiveData<ViewAction> = actionMutableLiveData
|
||||
|
||||
// This flag is used to ensure that the initial launch check is only performed once, so
|
||||
// that we can differentiate between a fresh launch and subsequent resumes for the connect
|
||||
// on start logic.
|
||||
private var hasPerformedInitialLaunchCheck = false
|
||||
|
||||
internal fun checkTunnelState(
|
||||
context: Context,
|
||||
isInitialLaunch: Boolean = false,
|
||||
@@ -41,15 +36,6 @@ internal class SplashViewModel
|
||||
// Stay a while and enjoy the logo
|
||||
delay(REQUEST_DELAY)
|
||||
|
||||
// We've already posted the initial action, so we can skip the rest of the checks
|
||||
if (isInitialLaunch && hasPerformedInitialLaunchCheck) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (isInitialLaunch) {
|
||||
hasPerformedInitialLaunchCheck = true
|
||||
}
|
||||
|
||||
// If we don't have VPN permission, we can't continue.
|
||||
if (!hasVpnPermissions(context) && applicationMode != ApplicationMode.TESTING) {
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToVpnPermission)
|
||||
@@ -57,7 +43,6 @@ internal class SplashViewModel
|
||||
}
|
||||
|
||||
val token = applicationRestrictions.getString("token") ?: repo.getTokenSync()
|
||||
val connectOnStart = repo.getConfigSync().connectOnStart
|
||||
|
||||
// If we don't have a token, we can't connect.
|
||||
if (token.isNullOrBlank()) {
|
||||
@@ -65,17 +50,25 @@ internal class SplashViewModel
|
||||
return@launch
|
||||
}
|
||||
|
||||
// If it's the initial launch but connect on start isn't enabled, we navigate to sign in.
|
||||
if (isInitialLaunch && !connectOnStart) {
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToSignIn)
|
||||
val isRunning = TunnelService.isRunning(context)
|
||||
|
||||
// If the service is already running, we can go directly to the session.
|
||||
if (isRunning) {
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToSession)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// If we reach here, we have a token and should attempt to connect.
|
||||
if (!TunnelService.isRunning(context)) {
|
||||
val connectOnStart = repo.getConfigSync().connectOnStart
|
||||
|
||||
// If this is the initial launch and connectOnStart is true, try to connect
|
||||
if (isInitialLaunch && connectOnStart) {
|
||||
TunnelService.start(context)
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToSession)
|
||||
return@launch
|
||||
}
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToSession)
|
||||
|
||||
// If we get here, we shouldn't start the tunnel, so show the sign in screen
|
||||
actionMutableLiveData.postValue(ViewAction.NavigateToSignIn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -320,6 +320,7 @@ class TunnelService : VpnService() {
|
||||
|
||||
stopNetworkMonitoring()
|
||||
stopDisconnectMonitoring()
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ export default function Android() {
|
||||
<Entries downloadLinks={downloadLinks} title="Android">
|
||||
{/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */}
|
||||
<Unreleased>
|
||||
<ChangeItem pull="9621">
|
||||
Fixes an issue where the VPN permission screen wouldn't dismiss after
|
||||
granting the VPN permission.
|
||||
</ChangeItem>
|
||||
<ChangeItem pull="9564">
|
||||
Fixes an issue where connections would fail to establish if both
|
||||
Client and Gateway were behind symmetric NAT.
|
||||
|
||||
Reference in New Issue
Block a user