diff --git a/kotlin/android/app/src/main/AndroidManifest.xml b/kotlin/android/app/src/main/AndroidManifest.xml
index eb8901661..e687b2497 100644
--- a/kotlin/android/app/src/main/AndroidManifest.xml
+++ b/kotlin/android/app/src/main/AndroidManifest.xml
@@ -72,6 +72,10 @@
android:name="dev.firezone.android.features.permission.vpn.ui.VpnPermissionActivity"
android:exported="false" />
+
+
when (action) {
AppLinkViewModel.ViewAction.AuthFlowComplete -> {
- // TODO: Continue starting the session showing sessionFragment
- Log.d("AppLinkHandlerActivity", "AuthFlowComplete")
-
- val intent = Intent(this@AppLinkHandlerActivity, MainActivity::class.java)
- this@AppLinkHandlerActivity.startActivity(intent)
- this@AppLinkHandlerActivity.finish()
+ startActivity(
+ Intent(this@AppLinkHandlerActivity, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ },
+ )
+ finish()
}
AppLinkViewModel.ViewAction.ShowError -> showError()
- else -> {
- Log.d("AppLinkHandlerActivity", "Unhandled action: $action")
- }
}
}
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/applink/ui/AppLinkViewModel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/applink/ui/AppLinkViewModel.kt
index b857f475c..1ca4629ea 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/applink/ui/AppLinkViewModel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/applink/ui/AppLinkViewModel.kt
@@ -41,7 +41,6 @@ internal class AppLinkViewModel
}
intent.data?.getQueryParameter(QUERY_CLIENT_AUTH_TOKEN)?.let { token ->
if (token.isNotBlank()) {
- // TODO: Don't log auth token
Log.d("AppLinkViewModel", "Found valid auth token in response")
saveTokenUseCase(token).collect()
} else {
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthActivity.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthActivity.kt
index e5f6549ac..dae1fbeef 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthActivity.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthActivity.kt
@@ -38,7 +38,7 @@ class AuthActivity : AppCompatActivity(R.layout.activity_auth) {
viewModel.actionLiveData.observe(this) { action ->
when (action) {
is AuthViewModel.ViewAction.LaunchAuthFlow -> setupWebView(action.url)
- is AuthViewModel.ViewAction.NavigateToSignInFragment -> {
+ is AuthViewModel.ViewAction.NavigateToSignIn -> {
navigateToSignIn()
}
is AuthViewModel.ViewAction.ShowError -> showError()
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthViewModel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthViewModel.kt
index 736e41f00..0c03a1f69 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthViewModel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/auth/ui/AuthViewModel.kt
@@ -39,7 +39,7 @@ internal class AuthViewModel
actionMutableLiveData.postValue(
if (authFlowLaunched || config.token != null) {
- ViewAction.NavigateToSignInFragment
+ ViewAction.NavigateToSignIn
} else {
authFlowLaunched = true
ViewAction.LaunchAuthFlow(
@@ -59,7 +59,7 @@ internal class AuthViewModel
internal sealed class ViewAction {
data class LaunchAuthFlow(val url: String) : ViewAction()
- object NavigateToSignInFragment : ViewAction()
+ object NavigateToSignIn : ViewAction()
object ShowError : ViewAction()
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionFragment.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionActivity.kt
similarity index 63%
rename from kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionFragment.kt
rename to kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionActivity.kt
index 8da8fa00f..88b65a313 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionFragment.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionActivity.kt
@@ -3,11 +3,9 @@ package dev.firezone.android.features.session.ui
import android.content.Intent
import android.os.Bundle
-import android.util.Log
-import android.view.View
+import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
+import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
@@ -17,41 +15,38 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.firezone.android.R
import dev.firezone.android.core.presentation.MainActivity
import dev.firezone.android.core.utils.ClipboardUtils
-import dev.firezone.android.databinding.FragmentSessionBinding
+import dev.firezone.android.databinding.ActivitySessionBinding
import kotlinx.coroutines.launch
@AndroidEntryPoint
-internal class SessionFragment : Fragment(R.layout.fragment_session) {
- private lateinit var binding: FragmentSessionBinding
+internal class SessionActivity : AppCompatActivity() {
+ private lateinit var binding: ActivitySessionBinding
private val viewModel: SessionViewModel by viewModels()
private val resourcesAdapter: ResourcesAdapter =
ResourcesAdapter { resource ->
- ClipboardUtils.copyToClipboard(requireContext(), resource.name, resource.address)
+ ClipboardUtils.copyToClipboard(this@SessionActivity, resource.name, resource.address)
}
- override fun onViewCreated(
- view: View,
- savedInstanceState: Bundle?,
- ) {
- super.onViewCreated(view, savedInstanceState)
- binding = FragmentSessionBinding.bind(view)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivitySessionBinding.inflate(layoutInflater)
+ setContentView(binding.root)
setupViews()
setupObservers()
- Log.d("SessionFragment", "Starting session...")
- viewModel.startSession()
+ viewModel.connect(this@SessionActivity)
}
private fun setupViews() {
binding.btSignOut.setOnClickListener {
- viewModel.onDisconnect()
+ viewModel.disconnect()
}
- val layoutManager = LinearLayoutManager(requireContext())
+ val layoutManager = LinearLayoutManager(this@SessionActivity)
val dividerItemDecoration =
DividerItemDecoration(
- requireContext(),
+ this@SessionActivity,
layoutManager.orientation,
)
binding.resourcesList.addItemDecoration(dividerItemDecoration)
@@ -60,15 +55,13 @@ internal class SessionFragment : Fragment(R.layout.fragment_session) {
}
private fun setupObservers() {
- viewModel.actionLiveData.observe(viewLifecycleOwner) { action ->
+ viewModel.actionLiveData.observe(this@SessionActivity) { action ->
when (action) {
- SessionViewModel.ViewAction.NavigateToSignInFragment -> {
- requireActivity().run {
- startActivity(
- Intent(this, MainActivity::class.java),
- )
- finish()
- }
+ SessionViewModel.ViewAction.NavigateToSignIn -> {
+ startActivity(
+ Intent(this, MainActivity::class.java),
+ )
+ finish()
}
SessionViewModel.ViewAction.ShowError -> showError()
}
@@ -86,7 +79,7 @@ internal class SessionFragment : Fragment(R.layout.fragment_session) {
}
private fun showError() {
- AlertDialog.Builder(requireContext())
+ AlertDialog.Builder(this@SessionActivity)
.setTitle(R.string.error_dialog_title)
.setMessage(R.string.error_dialog_message)
.setPositiveButton(
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionViewModel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionViewModel.kt
index f89dc8da2..c2813e334 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionViewModel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/session/ui/SessionViewModel.kt
@@ -1,6 +1,7 @@
/* Licensed under Apache 2.0 (C) 2023 Firezone, Inc. */
package dev.firezone.android.features.session.ui
+import android.content.Context
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@@ -8,7 +9,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.firezone.android.tunnel.TunnelManager
+import dev.firezone.android.tunnel.TunnelService
import dev.firezone.android.tunnel.callback.TunnelListener
+import dev.firezone.android.tunnel.data.TunnelRepository
import dev.firezone.android.tunnel.model.Resource
import dev.firezone.android.tunnel.model.Tunnel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -21,6 +24,7 @@ internal class SessionViewModel
@Inject
constructor(
private val tunnelManager: TunnelManager,
+ private val tunnelRepository: TunnelRepository,
) : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow = _uiState
@@ -31,7 +35,20 @@ internal class SessionViewModel
private val tunnelListener =
object : TunnelListener {
override fun onTunnelStateUpdate(state: Tunnel.State) {
- TODO("Not yet implemented")
+ when (state) {
+ Tunnel.State.Down -> {
+ onDisconnect()
+ }
+ Tunnel.State.Closed -> {
+ onClosed()
+ }
+ else -> {
+ _uiState.value =
+ _uiState.value.copy(
+ state = state,
+ )
+ }
+ }
}
override fun onResourcesUpdate(resources: List) {
@@ -48,10 +65,23 @@ internal class SessionViewModel
}
}
- fun startSession() {
+ fun connect(context: Context) {
viewModelScope.launch {
tunnelManager.addListener(tunnelListener)
- tunnelManager.connect()
+
+ val isServiceRunning = TunnelService.isRunning(context)
+ if (!isServiceRunning ||
+ tunnelRepository.getState() == Tunnel.State.Down ||
+ tunnelRepository.getState() == Tunnel.State.Closed
+ ) {
+ tunnelManager.connect()
+ } else {
+ _uiState.value =
+ _uiState.value.copy(
+ state = tunnelRepository.getState(),
+ resources = tunnelRepository.getResources(),
+ )
+ }
}
}
@@ -61,18 +91,26 @@ internal class SessionViewModel
tunnelManager.removeListener(tunnelListener)
}
- fun onDisconnect() {
+ fun disconnect() {
tunnelManager.disconnect()
+ }
+
+ private fun onDisconnect() {
+ // no-op
+ }
+
+ private fun onClosed() {
tunnelManager.removeListener(tunnelListener)
- actionMutableLiveData.postValue(ViewAction.NavigateToSignInFragment)
+ actionMutableLiveData.postValue(ViewAction.NavigateToSignIn)
}
internal data class UiState(
+ val state: Tunnel.State = Tunnel.State.Down,
val resources: List? = null,
)
internal sealed class ViewAction {
- object NavigateToSignInFragment : ViewAction()
+ object NavigateToSignIn : ViewAction()
object ShowError : ViewAction()
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsFragment.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsFragment.kt
index 5ac305242..f5cd4ce33 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsFragment.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsFragment.kt
@@ -43,7 +43,7 @@ internal class SettingsFragment : Fragment(R.layout.fragment_settings) {
private fun setupActionObservers() {
viewModel.actionLiveData.observe(viewLifecycleOwner) { action ->
when (action) {
- is SettingsViewModel.ViewAction.NavigateToSignInFragment ->
+ is SettingsViewModel.ViewAction.NavigateToSignIn ->
findNavController().navigate(
R.id.signInFragment,
)
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsViewModel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsViewModel.kt
index e3a3596eb..ef002ffbf 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsViewModel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/settings/ui/SettingsViewModel.kt
@@ -40,7 +40,7 @@ internal class SettingsViewModel
fun onSaveSettingsCompleted() {
viewModelScope.launch {
saveAccountIdUseCase(input).collect {
- actionMutableLiveData.postValue(ViewAction.NavigateToSignInFragment)
+ actionMutableLiveData.postValue(ViewAction.NavigateToSignIn)
}
}
}
@@ -59,7 +59,7 @@ internal class SettingsViewModel
}
internal sealed class ViewAction {
- object NavigateToSignInFragment : ViewAction()
+ object NavigateToSignIn : ViewAction()
data class FillAccountId(val value: String) : ViewAction()
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/signin/ui/SignInFragment.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/signin/ui/SignInFragment.kt
index 8a9f6ef18..7efb066e1 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/signin/ui/SignInFragment.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/signin/ui/SignInFragment.kt
@@ -11,7 +11,6 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.firezone.android.R
import dev.firezone.android.databinding.FragmentSignInBinding
import dev.firezone.android.features.auth.ui.AuthActivity
-import dev.firezone.android.features.splash.ui.SplashFragmentDirections
@AndroidEntryPoint
internal class SignInFragment : Fragment(R.layout.fragment_sign_in) {
@@ -37,10 +36,11 @@ internal class SignInFragment : Fragment(R.layout.fragment_sign_in) {
AuthActivity::class.java,
),
)
+ requireActivity().finish()
}
btSettings.setOnClickListener {
findNavController().navigate(
- SplashFragmentDirections.navigateToSettingsFragment(),
+ R.id.settingsFragment,
)
}
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashFragment.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashFragment.kt
index 8a644b69d..420068eb6 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashFragment.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashFragment.kt
@@ -1,6 +1,7 @@
/* Licensed under Apache 2.0 (C) 2023 Firezone, Inc. */
package dev.firezone.android.features.splash.ui
+import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
@@ -9,6 +10,7 @@ import androidx.navigation.fragment.findNavController
import dagger.hilt.android.AndroidEntryPoint
import dev.firezone.android.R
import dev.firezone.android.databinding.FragmentSplashBinding
+import dev.firezone.android.features.session.ui.SessionActivity
@AndroidEntryPoint
internal class SplashFragment : Fragment(R.layout.fragment_splash) {
@@ -37,17 +39,19 @@ internal class SplashFragment : Fragment(R.layout.fragment_splash) {
findNavController().navigate(
R.id.vpnPermissionActivity,
)
- SplashViewModel.ViewAction.NavigateToSignInFragment ->
+ SplashViewModel.ViewAction.NavigateToSignIn ->
findNavController().navigate(
R.id.signInFragment,
)
- SplashViewModel.ViewAction.NavigateToSettingsFragment ->
+ SplashViewModel.ViewAction.NavigateToSettings ->
findNavController().navigate(
R.id.settingsFragment,
)
- SplashViewModel.ViewAction.NavigateToSessionFragment ->
- findNavController().navigate(
- R.id.sessionFragment,
+ SplashViewModel.ViewAction.NavigateToSession ->
+ startActivity(
+ Intent(requireContext(), SessionActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ },
)
}
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashViewModel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashViewModel.kt
index b1f5cbad9..0990c1bac 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashViewModel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/features/splash/ui/SplashViewModel.kt
@@ -37,11 +37,11 @@ internal class SplashViewModel
}
.collect { user ->
if (user.accountId.isNullOrEmpty()) {
- actionMutableLiveData.postValue(ViewAction.NavigateToSettingsFragment)
+ actionMutableLiveData.postValue(ViewAction.NavigateToSettings)
} else if (user.token.isNullOrBlank()) {
- actionMutableLiveData.postValue(ViewAction.NavigateToSignInFragment)
+ actionMutableLiveData.postValue(ViewAction.NavigateToSignIn)
} else {
- actionMutableLiveData.postValue(ViewAction.NavigateToSessionFragment)
+ actionMutableLiveData.postValue(ViewAction.NavigateToSession)
}
}
}
@@ -54,8 +54,8 @@ internal class SplashViewModel
internal sealed class ViewAction {
object NavigateToVpnPermission : ViewAction()
- object NavigateToSettingsFragment : ViewAction()
- object NavigateToSignInFragment : ViewAction()
- object NavigateToSessionFragment : ViewAction()
+ object NavigateToSettings : ViewAction()
+ object NavigateToSignIn : ViewAction()
+ object NavigateToSession : ViewAction()
}
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelManager.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelManager.kt
index 8b7fd3c90..a70a4f80d 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelManager.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelManager.kt
@@ -8,7 +8,6 @@ import android.util.Log
import dev.firezone.android.core.data.PreferenceRepository
import dev.firezone.android.tunnel.callback.TunnelListener
import dev.firezone.android.tunnel.data.TunnelRepository
-import dev.firezone.android.tunnel.model.Tunnel
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Singleton
@@ -24,10 +23,17 @@ internal class TunnelManager
private val listeners: MutableSet> = mutableSetOf()
private val tunnelRepositoryListener =
- SharedPreferences.OnSharedPreferenceChangeListener { _, s ->
- if (s == TunnelRepository.RESOURCES_KEY) {
- listeners.forEach {
- it.get()?.onResourcesUpdate(tunnelRepository.getResources())
+ SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
+ when (key) {
+ TunnelRepository.STATE_KEY -> {
+ listeners.forEach {
+ it.get()?.onTunnelStateUpdate(tunnelRepository.getState())
+ }
+ }
+ TunnelRepository.RESOURCES_KEY -> {
+ listeners.forEach {
+ it.get()?.onResourcesUpdate(tunnelRepository.getResources())
+ }
}
}
}
@@ -43,7 +49,6 @@ internal class TunnelManager
}
tunnelRepository.addListener(tunnelRepositoryListener)
- tunnelRepository.setState(Tunnel.State.Connecting)
}
fun removeListener(listener: TunnelListener) {
@@ -65,6 +70,7 @@ internal class TunnelManager
fun disconnect() {
stopVPNService()
+ clearSessionData()
}
private fun startVPNService() {
@@ -77,8 +83,11 @@ internal class TunnelManager
val intent = Intent(appContext, TunnelService::class.java)
intent.action = TunnelService.ACTION_DISCONNECT
appContext.startService(intent)
- tunnelRepository.clearAll()
+ }
+
+ private fun clearSessionData() {
preferenceRepository.clearToken()
+ tunnelRepository.clearAll()
}
internal companion object {
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt
index 8c95c549a..3cd6df592 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt
@@ -1,10 +1,12 @@
/* Licensed under Apache 2.0 (C) 2023 Firezone, Inc. */
package dev.firezone.android.tunnel
+import android.app.ActivityManager
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
+import android.content.Context
import android.content.Intent
import android.net.VpnService
import android.system.OsConstants
@@ -16,6 +18,7 @@ import com.squareup.moshi.adapter
import dagger.hilt.android.AndroidEntryPoint
import dev.firezone.android.BuildConfig
import dev.firezone.android.R
+import dev.firezone.android.core.data.PreferenceRepository
import dev.firezone.android.core.domain.preference.GetConfigUseCase
import dev.firezone.android.core.presentation.MainActivity
import dev.firezone.android.tunnel.callback.ConnlibCallback
@@ -38,11 +41,16 @@ class TunnelService : VpnService() {
@Inject
internal lateinit var tunnelRepository: TunnelRepository
+ @Inject
+ internal lateinit var preferenceRepository: PreferenceRepository
+
@Inject
internal lateinit var moshi: Moshi
private var sessionPtr: Long? = null
+ private var shouldReconnect: Boolean = false
+
private val activeTunnel: Tunnel?
get() = tunnelRepository.get()
@@ -129,9 +137,9 @@ class TunnelService : VpnService() {
}
override fun onDisconnect(error: String?): Boolean {
- Log.d(TAG, "onDisconnect $error")
-
- onTunnelStateUpdate(Tunnel.State.Down)
+ onSessionDisconnected(
+ error = error?.takeUnless { it == "null" },
+ )
return true
}
}
@@ -169,11 +177,14 @@ class TunnelService : VpnService() {
try {
val config = getConfigUseCase.sync()
- Log.d("Connlib", "accountId: ${config.accountId}")
- Log.d("Connlib", "token: ${config.token}")
+ Log.d("Connlib", "connect(): accountId: ${config.accountId}")
+ if (tunnelRepository.getState() == Tunnel.State.Up) {
+ shouldReconnect = true
+ disconnect()
+ } else if (config.accountId != null && config.token != null) {
+ onTunnelStateUpdate(Tunnel.State.Connecting)
+ updateStatusNotification("Status: Connecting...")
- if (config.accountId != null && config.token != null) {
- Log.d("Connlib", "Attempting to establish TunnelSession...")
sessionPtr =
TunnelSession.connect(
controlPlaneUrl = BuildConfig.CONTROL_PLANE_URL,
@@ -183,33 +194,40 @@ class TunnelService : VpnService() {
logFilter = BuildConfig.CONNLIB_LOG_FILTER_STRING,
callback = callback,
)
- Log.d(TAG, "connlib session started! sessionPtr: $sessionPtr")
-
- onTunnelStateUpdate(Tunnel.State.Connecting)
-
- updateStatusNotification("Status: Connecting...")
}
} catch (exception: Exception) {
- Log.e(TAG, exception.message.toString())
+ Log.e(TAG, "connect(): " + exception.message.toString())
}
}
private fun disconnect() {
- Log.d(TAG, "Attempting to disconnect session")
+ Log.d(TAG, "disconnect(): Attempting to disconnect session")
try {
sessionPtr?.let {
- Log.d(TAG, "calling TunnelSession.disconnect")
TunnelSession.disconnect(it)
- }
+ } ?: onSessionDisconnected(null)
} catch (exception: Exception) {
Log.e(TAG, exception.message.toString())
}
- stopForeground(STOP_FOREGROUND_REMOVE)
+ }
+
+ private fun onSessionDisconnected(error: String?) {
+ sessionPtr = null
+ onTunnelStateUpdate(Tunnel.State.Down)
+
+ if (shouldReconnect && error == null) {
+ shouldReconnect = false
+ connect()
+ } else {
+ tunnelRepository.clearAll()
+ preferenceRepository.clearToken()
+ onTunnelStateUpdate(Tunnel.State.Closed)
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ }
}
private fun deviceId(): String {
val deviceId = FirebaseInstallations.getInstance().id
-
Log.d(TAG, "Device ID: $deviceId")
return deviceId.toString()
@@ -294,5 +312,16 @@ class TunnelService : VpnService() {
private const val TAG: String = "TunnelService"
private const val SESSION_NAME: String = "Firezone Connection"
private const val DEFAULT_MTU: Int = 1280
+
+ @SuppressWarnings("deprecation")
+ fun isRunning(context: Context): Boolean {
+ val manager = context.getSystemService(ACTIVITY_SERVICE) as ActivityManager
+ for (service in manager.getRunningServices(Int.MAX_VALUE)) {
+ if (TunnelService::class.java.name == service.service.className) {
+ return true
+ }
+ }
+ return false
+ }
}
}
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/data/TunnelRepositoryImpl.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/data/TunnelRepositoryImpl.kt
index f94b8384c..ef2540e34 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/data/TunnelRepositoryImpl.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/data/TunnelRepositoryImpl.kt
@@ -67,7 +67,7 @@ class TunnelRepositoryImpl
override fun getState(): Tunnel.State {
val json = sharedPreferences.getString(STATE_KEY, null)
- return json?.let { Tunnel.State.valueOf(it) } ?: Tunnel.State.Down
+ return json?.let { Tunnel.State.valueOf(it) } ?: Tunnel.State.Closed
}
override fun setResources(resources: List) {
diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/model/Tunnel.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/model/Tunnel.kt
index 216cc8951..a87272805 100644
--- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/model/Tunnel.kt
+++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/model/Tunnel.kt
@@ -14,8 +14,9 @@ data class Tunnel(
val resources: List = emptyList(),
) : Parcelable {
enum class State {
- Up,
Connecting,
+ Up,
Down,
+ Closed,
}
}
diff --git a/kotlin/android/app/src/main/res/layout/fragment_session.xml b/kotlin/android/app/src/main/res/layout/activity_session.xml
similarity index 97%
rename from kotlin/android/app/src/main/res/layout/fragment_session.xml
rename to kotlin/android/app/src/main/res/layout/activity_session.xml
index ec1b4cf06..38b5b3770 100644
--- a/kotlin/android/app/src/main/res/layout/fragment_session.xml
+++ b/kotlin/android/app/src/main/res/layout/activity_session.xml
@@ -53,7 +53,7 @@
android:id="@+id/btSignOut"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/sign_out_activity_button_text"
+ android:text="@string/sign_out"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/kotlin/android/app/src/main/res/layout/fragment_settings.xml b/kotlin/android/app/src/main/res/layout/fragment_settings.xml
index cd47fc28f..4e13b09ed 100644
--- a/kotlin/android/app/src/main/res/layout/fragment_settings.xml
+++ b/kotlin/android/app/src/main/res/layout/fragment_settings.xml
@@ -47,7 +47,7 @@
android:id="@+id/etInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="@string/settings_fragment_input_hint"
+ android:hint="@string/account_id"
android:importantForAutofill="no"
android:inputType="text" />
@@ -58,7 +58,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:enabled="false"
- android:text="@string/settings_fragment_button_text"
+ android:text="@string/save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/kotlin/android/app/src/main/res/layout/fragment_sign_in.xml b/kotlin/android/app/src/main/res/layout/fragment_sign_in.xml
index ebf096b12..dae93dd55 100644
--- a/kotlin/android/app/src/main/res/layout/fragment_sign_in.xml
+++ b/kotlin/android/app/src/main/res/layout/fragment_sign_in.xml
@@ -39,7 +39,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/spacing_small"
- android:text="@string/sign_in_fragment_sign_status_text"
+ android:text="@string/signed_out"
app:layout_constraintBottom_toTopOf="@+id/btSignIn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -49,7 +49,7 @@
android:id="@+id/btSignIn"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/sign_in_fragment_button_text"
+ android:text="@string/sign_in"
app:layout_constraintBottom_toTopOf="@+id/btSettings"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
@@ -58,7 +58,7 @@
android:id="@+id/btSettings"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/sign_in_settings_button_text"
+ android:text="@string/settings"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/kotlin/android/app/src/main/res/navigation/app_nav_graph.xml b/kotlin/android/app/src/main/res/navigation/app_nav_graph.xml
index 45284e88a..2846af408 100644
--- a/kotlin/android/app/src/main/res/navigation/app_nav_graph.xml
+++ b/kotlin/android/app/src/main/res/navigation/app_nav_graph.xml
@@ -8,85 +8,22 @@
-
-
-
-
-
-
-
-
+ tools:layout="@layout/fragment_splash" />
-
-
-
+ tools:layout="@layout/fragment_settings" />
+ tools:layout="@layout/fragment_sign_in" />
-
-
-
-
-
-
-
-
-
-
-
+
firezone
-
- Login URL
- account-id
- Save
+
+ account-id
+ Save
-
- Sign In
- Settings
- Signed Out
+
+ Sign In
+ Settings
+ Signed Out
-
+
Resources
- Sign Out
+ Sign Out
-
+
Launching Auth Flow…