diff --git a/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt b/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt index d4d7690b..f1592f7e 100644 --- a/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt +++ b/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt @@ -46,10 +46,12 @@ import nl.eduvpn.app.service.VPNConnectionService import nl.eduvpn.app.service.VPNService.VPNStatus import nl.eduvpn.app.utils.ErrorDialog import nl.eduvpn.app.utils.FormattingUtils +import nl.eduvpn.app.utils.Log import nl.eduvpn.app.viewmodel.BaseConnectionViewModel import nl.eduvpn.app.viewmodel.ConnectionStatusViewModel import nl.eduvpn.app.viewmodel.MainViewModel import org.eduvpn.common.Protocol +import java.util.logging.Logger /** * The fragment which displays the status of the current connection. @@ -74,10 +76,11 @@ class ConnectionStatusFragment : BaseFragment() binding.failoverNeeded = mainViewModel.failoverResult.value ?: false binding.secondsConnected = viewModel.connectionTimeLiveData.map { secondsConnected -> val context = this@ConnectionStatusFragment.context ?: return@map null - FormattingUtils.formatDurationSeconds( - context, - secondsConnected - ) + if (secondsConnected < 0) { + FormattingUtils.formatDurationSeconds(context, null) + } else { + FormattingUtils.formatDurationSeconds(context, secondsConnected) + } } binding.bytesDownloaded = viewModel.byteCountFlow.map { bc -> val context = this@ConnectionStatusFragment.context ?: return@map null @@ -300,18 +303,29 @@ class ConnectionStatusFragment : BaseFragment() viewModel.isInDisconnectMode.value = false setToggleCheckedWithoutAction(true) viewModel.viewModelScope.launch(Dispatchers.IO) { - viewModel.selectProfileToConnectTo(profile, preferTcp = false).onFailure { thr -> - withContext(Dispatchers.Main) { - setToggleCheckedWithoutAction(false) - viewModel.isInDisconnectMode.value = true - ErrorDialog.show(requireActivity(), thr) + try { + viewModel.selectProfileToConnectTo(profile, preferTcp = false).onFailure { thr -> + withContext(Dispatchers.Main) { + setToggleCheckedWithoutAction(false) + viewModel.isInDisconnectMode.value = true + ErrorDialog.show(requireActivity(), thr) + } + }.onSuccess { + // Relaunch this fragment. This is required, because in some cases, the backend + // implementation (WireGuard vs OpenVPN) might be different, and we would be connected + // to the incorrect one. + (activity as? MainActivity)?.openFragment(ConnectionStatusFragment(), openOnTop = false) + } + } catch (ex: Exception) { + Log.w(TAG, "Could not select profile to connect to!", ex) + activity?.let { + ErrorDialog.show(it, ex) } - }.onSuccess { - // Relaunch this fragment. This is required, because in some cases, the backend - // implementation (WireGuard vs OpenVPN) might be different, and we would be connected - // to the incorrect one. - (activity as? MainActivity)?.openFragment(ConnectionStatusFragment(), openOnTop = false) } } } + + companion object { + private val TAG = ConnectionStatusFragment::class.java.name + } } diff --git a/app/src/main/java/nl/eduvpn/app/fragment/OrganizationSelectionFragment.kt b/app/src/main/java/nl/eduvpn/app/fragment/OrganizationSelectionFragment.kt index b0d352f2..2d905755 100644 --- a/app/src/main/java/nl/eduvpn/app/fragment/OrganizationSelectionFragment.kt +++ b/app/src/main/java/nl/eduvpn/app/fragment/OrganizationSelectionFragment.kt @@ -130,7 +130,9 @@ class OrganizationSelectionFragment : BaseFragment when (parentAction) { is BaseConnectionViewModel.ParentAction.DisplayError -> { - ErrorDialog.show(requireActivity(), parentAction.title, parentAction.message) + activity?.let { activity -> + ErrorDialog.show(activity, parentAction.title, parentAction.message) + } } is BaseConnectionViewModel.ParentAction.ShowContextCanceledToast -> { Snackbar.make(view, parentAction.message, Snackbar.LENGTH_LONG).show() @@ -143,7 +145,9 @@ class OrganizationSelectionFragment : BaseFragment if (exception != null) { - ErrorDialog.show(requireActivity(), exception) + activity?.let { activity -> + ErrorDialog.show(activity, exception) + } } } } diff --git a/app/src/main/java/nl/eduvpn/app/inject/ApplicationModule.kt b/app/src/main/java/nl/eduvpn/app/inject/ApplicationModule.kt index d2debc7e..d205f557 100644 --- a/app/src/main/java/nl/eduvpn/app/inject/ApplicationModule.kt +++ b/app/src/main/java/nl/eduvpn/app/inject/ApplicationModule.kt @@ -112,7 +112,7 @@ class ApplicationModule(private val application: EduVPNApplication) { fun provideConnectionTimeLiveData( vpnService: VPNService, @Named("timer") timer: LiveData - ): LiveData { + ): LiveData { return ConnectionTimeLiveData.create(vpnService, timer) } diff --git a/app/src/main/java/nl/eduvpn/app/livedata/ConnectionTimeLiveData.kt b/app/src/main/java/nl/eduvpn/app/livedata/ConnectionTimeLiveData.kt index 78eae1d6..cf84309f 100644 --- a/app/src/main/java/nl/eduvpn/app/livedata/ConnectionTimeLiveData.kt +++ b/app/src/main/java/nl/eduvpn/app/livedata/ConnectionTimeLiveData.kt @@ -29,10 +29,10 @@ object ConnectionTimeLiveData { fun create( vpnStatusLiveData: LiveData, timer: LiveData - ): LiveData { + ): LiveData { var connectionTime = 0L - val connectionTimeLiveData = MediatorLiveData() + val connectionTimeLiveData = MediatorLiveData() val update = { connectionTimeLiveData.value = (System.currentTimeMillis() - connectionTime) / 1000L @@ -52,7 +52,7 @@ object ConnectionTimeLiveData { } } else if (vpnStatus == VPNService.VPNStatus.DISCONNECTED) { connectionTimeLiveData.removeSource(timer) - connectionTimeLiveData.value = null + connectionTimeLiveData.value = -1 } } diff --git a/app/src/main/java/nl/eduvpn/app/service/BackendService.kt b/app/src/main/java/nl/eduvpn/app/service/BackendService.kt index 642371e5..908f2baa 100644 --- a/app/src/main/java/nl/eduvpn/app/service/BackendService.kt +++ b/app/src/main/java/nl/eduvpn/app/service/BackendService.kt @@ -233,13 +233,12 @@ class BackendService( return true } - @kotlin.jvm.Throws(CommonException::class, UnknownFormatException::class) - suspend fun getAddedServers(): AddedServers = withContext(Dispatchers.IO) { + fun getAddedServers(): AddedServers { val dataErrorTuple = goBackend.addedServers if (dataErrorTuple.isError) { throw CommonException(dataErrorTuple.error) } - serializerService.deserializeAddedServers(dataErrorTuple.data) + return serializerService.deserializeAddedServers(dataErrorTuple.data) } @kotlin.jvm.Throws(CommonException::class, UnknownFormatException::class) diff --git a/app/src/main/java/nl/eduvpn/app/service/EduVPNOpenVPNService.java b/app/src/main/java/nl/eduvpn/app/service/EduVPNOpenVPNService.java index e0354c03..7633783f 100644 --- a/app/src/main/java/nl/eduvpn/app/service/EduVPNOpenVPNService.java +++ b/app/src/main/java/nl/eduvpn/app/service/EduVPNOpenVPNService.java @@ -232,7 +232,17 @@ public void startForeground(int id, @NonNull Notification notification) { */ public void disconnect() { try { - _openVPNService.stopVPN(false); + if (_openVPNService == null) { + ConnectionStatus previousStatus = _connectionStatus; + _connectionStatus = ConnectionStatus.LEVEL_NOTCONNECTED; + if (previousStatus != _connectionStatus) { + _updatesHandler.post(() -> { + setValue(connectionStatusToVPNStatus(_connectionStatus)); + }); + } + } else { + _openVPNService.stopVPN(false); + } } catch (RemoteException ex) { Log.e(TAG, "Exception when trying to stop connection. Connection might not be closed!", ex); } diff --git a/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt b/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt index 8e70d2b9..d0234083 100644 --- a/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt +++ b/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt @@ -42,7 +42,7 @@ class HistoryService(private val backendService: BackendService) { * Loads the state of the service. */ @kotlin.jvm.Throws(Exception::class) - suspend fun load() { + fun load() { try { addedServers = backendService.getAddedServers() notifyListeners() @@ -90,7 +90,7 @@ class HistoryService(private val backendService: BackendService) { * @param instance The instance to remove the data for. */ @Throws(CommonException::class) - suspend fun removeAllDataForInstance(instance: Instance) { + fun removeAllDataForInstance(instance: Instance) { backendService.removeServer(instance) load() notifyListeners() diff --git a/app/src/main/java/nl/eduvpn/app/utils/ErrorDialog.kt b/app/src/main/java/nl/eduvpn/app/utils/ErrorDialog.kt index cf5d5567..350ea4cf 100644 --- a/app/src/main/java/nl/eduvpn/app/utils/ErrorDialog.kt +++ b/app/src/main/java/nl/eduvpn/app/utils/ErrorDialog.kt @@ -99,7 +99,9 @@ object ErrorDialog { putString(ErrorDialogFragment.ARGS_KEY_TITLE, title) putString(ErrorDialogFragment.ARGS_KEY_MESSAGE, message) } - dialog.show(activity.supportFragmentManager, null) + if (!activity.isFinishing && !activity.supportFragmentManager.isStateSaved) { + dialog.show(activity.supportFragmentManager, null) + } return dialog } diff --git a/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt b/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt index 09c6d007..70248fe5 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt @@ -33,8 +33,6 @@ import nl.eduvpn.app.livedata.toSingleEvent import nl.eduvpn.app.service.* import nl.eduvpn.app.utils.Log import nl.eduvpn.app.utils.runCatchingCoroutine -import java.text.ParseException -import java.text.SimpleDateFormat import java.util.* /** @@ -142,17 +140,6 @@ abstract class BaseConnectionViewModel( } } - private fun showError(thr: Throwable?, resourceId: Int): Result { - val message = context.getString(resourceId, thr) - Log.e(TAG, message, thr) - connectionState.value = ConnectionState.Ready - _parentAction.value = ParentAction.DisplayError( - R.string.error_dialog_title, - message - ) - return Result.failure(thr ?: RuntimeException(message)) - } - fun disconnectWithCall(vpnService: VPNService) { vpnConnectionService.disconnect(context, vpnService) } diff --git a/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt b/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt index a5ef5ecb..fd7cd05f 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt @@ -55,7 +55,7 @@ class ConnectionStatusViewModel @Inject constructor( @Named("timer") val timer: LiveData, @Named("connectionTimeLiveData") - val connectionTimeLiveData: LiveData, + val connectionTimeLiveData: LiveData, private val backendService: BackendService, vpnConnectionService: VPNConnectionService, ) : BaseConnectionViewModel( diff --git a/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt b/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt index 8408797e..5c55748d 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt @@ -97,13 +97,11 @@ class MainViewModel @Inject constructor( _mainParentAction.postValue(MainParentAction.OnProxyGuardReady) } ) - viewModelScope.launch(Dispatchers.IO) { - try { - historyService.load() - } catch (ex: Exception) { - Log.w(TAG, "Could not load history from the common backend on initialization!", ex) - _mainParentAction.postValue(MainParentAction.ShowError(ex)) - } + try { + historyService.load() + } catch (ex: Exception) { + Log.w(TAG, "Could not load history from the common backend on initialization!", ex) + _mainParentAction.postValue(MainParentAction.ShowError(ex)) } } diff --git a/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt b/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt index c51d6c03..e2133d9b 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt @@ -85,11 +85,13 @@ class ServerSelectionViewModel @Inject constructor( private fun refresh() { viewModelScope.launch(Dispatchers.IO) { + println("XXX Starting refresh") try { historyService.removeListener(this@ServerSelectionViewModel) historyService.load() historyService.addListener(this@ServerSelectionViewModel) + println("XXX Added server in SSM ${historyService.addedServers?.customServers}") val needsServerList = historyService.addedServers?.secureInternetServer != null if (needsServerList) { refreshServerList() @@ -97,6 +99,7 @@ class ServerSelectionViewModel @Inject constructor( refreshInstances() } } catch (ex: Exception) { + Log.w(TAG, "XXX Could not refresh server selection list", ex) _parentAction.postValue(ParentAction.DisplayError(R.string.error_dialog_title, ex.message ?: ex.toString())) } }