From 66b76f9485b3eee52318ae9e706eb7c43312b927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Zolnai?= Date: Thu, 17 Oct 2024 13:54:25 +0200 Subject: [PATCH 1/3] Update, WG, ics-openvpn, common submodules --- app/src/main/AndroidManifest.xml | 21 ++-------- .../java/nl/eduvpn/app/OpenVpnLogsActivity.kt | 41 +++++++++++++++++++ .../eduvpn/app/fragment/SettingsFragment.kt | 3 +- .../nl/eduvpn/app/inject/EduVPNComponent.kt | 2 + app/src/main/res/layout/activity_api_logs.xml | 2 +- .../main/res/layout/activity_openvpn_logs.xml | 37 +++++++++++++++++ common/libs/eduvpn-common | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- ics-openvpn | 2 +- wireguard | 2 +- 10 files changed, 90 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/nl/eduvpn/app/OpenVpnLogsActivity.kt create mode 100644 app/src/main/res/layout/activity_openvpn_logs.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ced936b9..1ae60077 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -142,25 +142,10 @@ - - - - - - - - + android:exported="false" + android:theme="@style/AppTheme.NoActionBar" /> () { + + override val layout = R.layout.activity_openvpn_logs + + @Inject + protected lateinit var viewModelFactory: ViewModelFactory + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + EduVPNApplication.get(this).component().inject(this) + setSupportActionBar(binding.toolbar.toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + binding.toolbar.settingsButton.isVisible = false + supportFragmentManager.beginTransaction() + .add(binding.fragmentContainer.id, LogFragment()) + .commit() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (item.itemId == android.R.id.home) { + onBackPressedDispatcher.onBackPressed() + true + } else { + super.onOptionsItemSelected(item) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/nl/eduvpn/app/fragment/SettingsFragment.kt b/app/src/main/java/nl/eduvpn/app/fragment/SettingsFragment.kt index 952908fe..7b8fcedc 100644 --- a/app/src/main/java/nl/eduvpn/app/fragment/SettingsFragment.kt +++ b/app/src/main/java/nl/eduvpn/app/fragment/SettingsFragment.kt @@ -28,6 +28,7 @@ import nl.eduvpn.app.ApiLogsActivity import nl.eduvpn.app.BuildConfig import nl.eduvpn.app.EduVPNApplication import nl.eduvpn.app.LicenseActivity +import nl.eduvpn.app.OpenVpnLogsActivity import nl.eduvpn.app.R import nl.eduvpn.app.SettingsActivity import nl.eduvpn.app.base.BaseFragment @@ -57,7 +58,7 @@ class SettingsFragment : BaseFragment() { } binding.resetDataButton.setOnClickListener { onResetDataClicked() } binding.viewOpenvpnLogsButton.setOnClickListener { - val intent = Intent(activity, LogWindow::class.java) + val intent = Intent(activity, OpenVpnLogsActivity::class.java) startActivity(intent) } binding.viewApiLogsButton.setOnClickListener { diff --git a/app/src/main/java/nl/eduvpn/app/inject/EduVPNComponent.kt b/app/src/main/java/nl/eduvpn/app/inject/EduVPNComponent.kt index a829d7b3..00ae6744 100644 --- a/app/src/main/java/nl/eduvpn/app/inject/EduVPNComponent.kt +++ b/app/src/main/java/nl/eduvpn/app/inject/EduVPNComponent.kt @@ -22,6 +22,7 @@ import nl.eduvpn.app.CertExpiredBroadcastReceiver import nl.eduvpn.app.DisconnectVPNBroadcastReceiver import nl.eduvpn.app.EduVPNApplication import nl.eduvpn.app.MainActivity +import nl.eduvpn.app.OpenVpnLogsActivity import nl.eduvpn.app.fragment.* import javax.inject.Singleton @@ -43,6 +44,7 @@ interface EduVPNComponent { fun inject(organizationSelectionFragment: OrganizationSelectionFragment) fun inject(mainActivity: MainActivity) fun inject(apiLogsActivity: ApiLogsActivity) + fun inject(apiLogsActivity: OpenVpnLogsActivity) fun inject(connectionStatusFragment: ConnectionStatusFragment) fun inject(homeFragment: ProfileSelectionFragment) fun inject(settingsFragment: SettingsFragment) diff --git a/app/src/main/res/layout/activity_api_logs.xml b/app/src/main/res/layout/activity_api_logs.xml index 8295b34c..fe2b2095 100644 --- a/app/src/main/res/layout/activity_api_logs.xml +++ b/app/src/main/res/layout/activity_api_logs.xml @@ -22,7 +22,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" - tools:context=".LicenseActivity"> + tools:context=".ApiLogsActivity"> + + + + + + + + + \ No newline at end of file diff --git a/common/libs/eduvpn-common b/common/libs/eduvpn-common index 4962d0e2..f3c04d56 160000 --- a/common/libs/eduvpn-common +++ b/common/libs/eduvpn-common @@ -1 +1 @@ -Subproject commit 4962d0e2d37e93af20c8afdae6f59d4656f272f3 +Subproject commit f3c04d56ca67c2ff0b08bf75cc51ea8be14c84b9 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48c0a02c..1e2fbf0d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/ics-openvpn b/ics-openvpn index 3520bde9..4d795041 160000 --- a/ics-openvpn +++ b/ics-openvpn @@ -1 +1 @@ -Subproject commit 3520bde975dede6d3cd5a65278312b0ecf1014c1 +Subproject commit 4d7950419f470b2c37502d94b2ea7dd890f237a4 diff --git a/wireguard b/wireguard index 2055ab96..d3513aa6 160000 --- a/wireguard +++ b/wireguard @@ -1 +1 @@ -Subproject commit 2055ab9671aa6d8eeee0567f45449eb3237f8b8f +Subproject commit d3513aa6a87be29d93a1c839fa7ec8442b104c7d From 5e0d5d965f93acab282fb1ed57259c265b1b2622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Zolnai?= Date: Mon, 14 Oct 2024 16:17:49 +0200 Subject: [PATCH 2/3] Move backend methods to background threads --- .../app/fragment/ProfileSelectionFragment.kt | 2 +- .../nl/eduvpn/app/service/BackendService.kt | 6 +- .../nl/eduvpn/app/service/HistoryService.java | 135 ------------------ .../nl/eduvpn/app/service/HistoryService.kt | 121 ++++++++++++++++ .../app/viewmodel/BaseConnectionViewModel.kt | 4 +- .../viewmodel/ConnectionStatusViewModel.kt | 8 +- .../nl/eduvpn/app/viewmodel/MainViewModel.kt | 28 +++- .../app/viewmodel/ServerSelectionViewModel.kt | 25 ++-- .../eduvpn/app/viewmodel/SettingsViewModel.kt | 9 +- app/src/main/res/values-ca/strings.xml | 4 - app/src/main/res/values-de/strings.xml | 4 - app/src/main/res/values-es/strings.xml | 4 - app/src/main/res/values/strings.xml | 4 - 13 files changed, 177 insertions(+), 177 deletions(-) delete mode 100644 app/src/main/java/nl/eduvpn/app/service/HistoryService.java create mode 100644 app/src/main/java/nl/eduvpn/app/service/HistoryService.kt diff --git a/app/src/main/java/nl/eduvpn/app/fragment/ProfileSelectionFragment.kt b/app/src/main/java/nl/eduvpn/app/fragment/ProfileSelectionFragment.kt index 66c9746b..4ffdc075 100644 --- a/app/src/main/java/nl/eduvpn/app/fragment/ProfileSelectionFragment.kt +++ b/app/src/main/java/nl/eduvpn/app/fragment/ProfileSelectionFragment.kt @@ -92,7 +92,7 @@ class ProfileSelectionFragment : BaseFragment() } private fun selectProfileToConnectTo(profile: Profile) { - viewModel.viewModelScope.launch { + viewModel.viewModelScope.launch(Dispatchers.IO) { viewModel.selectProfileToConnectTo(profile, preferTcp = false).onFailure { thr -> withContext(Dispatchers.Main) { ErrorDialog.show(requireActivity(), thr) 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 38f32b51..642371e5 100644 --- a/app/src/main/java/nl/eduvpn/app/service/BackendService.kt +++ b/app/src/main/java/nl/eduvpn/app/service/BackendService.kt @@ -176,7 +176,7 @@ class BackendService( } @kotlin.jvm.Throws(CommonException::class) - suspend fun addServer(instance: Instance) { + fun addServer(instance: Instance) { val errorString = goBackend.addServer( instance.authorizationType.toNativeServerType().nativeValue, instance.baseURI @@ -234,12 +234,12 @@ class BackendService( } @kotlin.jvm.Throws(CommonException::class, UnknownFormatException::class) - fun getAddedServers(): AddedServers { + suspend fun getAddedServers(): AddedServers = withContext(Dispatchers.IO) { val dataErrorTuple = goBackend.addedServers if (dataErrorTuple.isError) { throw CommonException(dataErrorTuple.error) } - return serializerService.deserializeAddedServers(dataErrorTuple.data) + serializerService.deserializeAddedServers(dataErrorTuple.data) } @kotlin.jvm.Throws(CommonException::class, UnknownFormatException::class) diff --git a/app/src/main/java/nl/eduvpn/app/service/HistoryService.java b/app/src/main/java/nl/eduvpn/app/service/HistoryService.java deleted file mode 100644 index 22939d78..00000000 --- a/app/src/main/java/nl/eduvpn/app/service/HistoryService.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of eduVPN. - * - * eduVPN is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * eduVPN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with eduVPN. If not, see . - */ - -package nl.eduvpn.app.service; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; - -import nl.eduvpn.app.entity.AddedServers; -import nl.eduvpn.app.entity.CertExpiryTimes; -import nl.eduvpn.app.entity.CurrentServer; -import nl.eduvpn.app.entity.Instance; -import nl.eduvpn.app.entity.Organization; -import nl.eduvpn.app.entity.OrganizationList; -import nl.eduvpn.app.entity.exception.CommonException; -import nl.eduvpn.app.utils.Listener; -import nl.eduvpn.app.utils.Log; - -/** - * Service which stores previously used access token and profile names. - * This allows us to skip some steps, which will make the user experience more fluid. - * Created by Daniel Zolnai on 2016-10-20. - */ -public class HistoryService { - private static final String TAG = HistoryService.class.getName(); - private AddedServers _addedServers = null; - - private final BackendService _backendService; - - private final List _listeners = new LinkedList<>(); - - /** - * Constructor. - */ - public HistoryService(@NonNull BackendService backendService) { - _backendService = backendService; - } - - /** - * Loads the state of the service. - */ - public void load() { - try { - _addedServers = _backendService.getAddedServers(); - notifyListeners(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public void addListener(Listener listener) { - if (!_listeners.contains(listener)) { - _listeners.add(listener); - } - } - - public void removeListener(Listener listener) { - _listeners.remove(listener); - } - - private void notifyListeners() { - _listeners.forEach(l -> l.update(this, null)); - } - - - public @Nullable AddedServers getAddedServers() { - return _addedServers; - } - - public CurrentServer getCurrentServer() { - return _backendService.getCurrentServer(); - } - - public @Nullable CertExpiryTimes getCertExpiryTimes() { - try { - return _backendService.getCertExpiryTimes(); - } catch (Exception ex) { - Log.w(TAG, "Could not determine cert expiry times!", ex); - return null; - } - } - - - public boolean hasSecureInternetServer() { - return _addedServers.getSecureInternetServer() != null; - } - - /** - * Removes all saved data for an instance. - * - * @param instance The instance to remove the data for. - */ - public void removeAllDataForInstance(Instance instance) throws CommonException { - _backendService.removeServer(instance); - load(); - notifyListeners(); - } - - /*** - * Removes all saved data in this app. - ***/ - public void removeOrganizationData() throws CommonException, SerializerService.UnknownFormatException { - List instancesToRemove = _backendService.getAddedServers().asInstances(); - CommonException errorThrown = null; - for (Instance instance : instancesToRemove) { - try { - removeAllDataForInstance(instance); - } catch (CommonException ex) { - errorThrown = ex; - } - } - if (errorThrown != null) { - throw errorThrown; - } - } -} diff --git a/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt b/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt new file mode 100644 index 00000000..8e70d2b9 --- /dev/null +++ b/app/src/main/java/nl/eduvpn/app/service/HistoryService.kt @@ -0,0 +1,121 @@ +/* + * This file is part of eduVPN. + * + * eduVPN is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * eduVPN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eduVPN. If not, see . + */ +package nl.eduvpn.app.service + +import nl.eduvpn.app.entity.AddedServers +import nl.eduvpn.app.entity.CertExpiryTimes +import nl.eduvpn.app.entity.CurrentServer +import nl.eduvpn.app.entity.Instance +import nl.eduvpn.app.entity.exception.CommonException +import nl.eduvpn.app.service.SerializerService.UnknownFormatException +import nl.eduvpn.app.utils.Listener +import nl.eduvpn.app.utils.Log +import java.util.LinkedList +import java.util.function.Consumer + +/** + * Service which stores previously used access token and profile names. + * This allows us to skip some steps, which will make the user experience more fluid. + * Created by Daniel Zolnai on 2016-10-20. + */ +class HistoryService(private val backendService: BackendService) { + var addedServers: AddedServers? = null + private set + + private val _listeners: MutableList = LinkedList() + + /** + * Loads the state of the service. + */ + @kotlin.jvm.Throws(Exception::class) + suspend fun load() { + try { + addedServers = backendService.getAddedServers() + notifyListeners() + } catch (e: Exception) { + throw RuntimeException(e) + } + } + + fun addListener(listener: Listener) { + if (!_listeners.contains(listener)) { + _listeners.add(listener) + } + } + + fun removeListener(listener: Listener) { + _listeners.remove(listener) + } + + private fun notifyListeners() { + _listeners.forEach(Consumer { l: Listener -> l.update(this, null) }) + } + + + val currentServer: CurrentServer? + get() = backendService.getCurrentServer() + + val certExpiryTimes: CertExpiryTimes? + get() { + try { + return backendService.getCertExpiryTimes() + } catch (ex: Exception) { + Log.w(TAG, "Could not determine cert expiry times!", ex) + return null + } + } + + + fun hasSecureInternetServer(): Boolean { + return addedServers?.secureInternetServer != null + } + + /** + * Removes all saved data for an instance. + * + * @param instance The instance to remove the data for. + */ + @Throws(CommonException::class) + suspend fun removeAllDataForInstance(instance: Instance) { + backendService.removeServer(instance) + load() + notifyListeners() + } + + /*** + * Removes all saved data in this app. + */ + @Throws(CommonException::class, UnknownFormatException::class) + suspend fun removeOrganizationData() { + val instancesToRemove = backendService.getAddedServers().asInstances() + var errorThrown: CommonException? = null + for (instance in instancesToRemove) { + try { + removeAllDataForInstance(instance) + } catch (ex: CommonException) { + errorThrown = ex + } + } + if (errorThrown != null) { + throw errorThrown + } + } + + companion object { + private val TAG: String = HistoryService::class.java.name + } +} 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 1db62c3e..09c6d007 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/BaseConnectionViewModel.kt @@ -158,7 +158,9 @@ abstract class BaseConnectionViewModel( } fun deleteAllDataForInstance(instance: Instance) { - historyService.removeAllDataForInstance(instance) + viewModelScope.launch(Dispatchers.IO) { + historyService.removeAllDataForInstance(instance) + } } fun getProfileInstance(): Instance { 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 11cfdd8c..a5ef5ecb 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/ConnectionStatusViewModel.kt @@ -99,7 +99,7 @@ class ConnectionStatusViewModel @Inject constructor( init { val currentServer = historyService.currentServer certExpiryTimes = historyService.certExpiryTimes - val connectionInstance = currentServer.asInstance() + val connectionInstance = currentServer?.asInstance() if (connectionInstance != null && connectionInstance.supportContact.isNotEmpty()) { val supportContacts = StringBuilder() for (contact in connectionInstance.supportContact) { @@ -134,9 +134,9 @@ class ConnectionStatusViewModel @Inject constructor( canRenew.postValue(true) } } - serverProfiles.value = currentServer.getProfiles() - profileName.value = currentServer.currentProfile?.displayName?.bestTranslation - serverName.value = currentServer.getDisplayName()?.bestTranslation + serverProfiles.value = currentServer?.getProfiles() + profileName.value = currentServer?.currentProfile?.displayName?.bestTranslation + serverName.value = currentServer?.getDisplayName()?.bestTranslation viewModelScope.launch { vpnService.asFlow().collect { status -> val previousStatus = vpnStatus.value ?: VPNService.VPNStatus.DISCONNECTED 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 21d73f0f..8408797e 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt @@ -97,12 +97,26 @@ class MainViewModel @Inject constructor( _mainParentAction.postValue(MainParentAction.OnProxyGuardReady) } ) - historyService.load() + 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)) + } + } } override fun onResume() { - historyService.load() - backendService.cancelPendingRedirect() + viewModelScope.launch(Dispatchers.IO) { + try { + historyService.load() + backendService.cancelPendingRedirect() + } catch (ex: Exception) { + Log.w(TAG, "Could not load history from the common backend on resume!", ex) + _mainParentAction.postValue(MainParentAction.ShowError(ex)) + } + } super.onResume() } @@ -153,7 +167,7 @@ class MainViewModel @Inject constructor( return } } else { - throw IllegalArgumentException("Unexpected protocol type: ${protocol}") + throw IllegalArgumentException("Unexpected protocol type: $protocol") } val service = vpnConnectionService.connectionToConfig(viewModelScope, activity, parsedConfig, preferTcp) if (protocol == Protocol.WireGuard.nativeValue && !preferTcp && config.shouldFailover) { @@ -205,7 +219,11 @@ class MainViewModel @Inject constructor( try { backendService.selectCountry(cookie, organizationId, countryCode) withContext(Dispatchers.Main) { - historyService.load() + try { + historyService.load() + } catch (ex: Exception) { + _mainParentAction.postValue(MainParentAction.ShowError(ex)) + } } } catch (ex: Exception) { _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 2911a636..c51d6c03 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/ServerSelectionViewModel.kt @@ -84,14 +84,21 @@ class ServerSelectionViewModel @Inject constructor( } private fun refresh() { - historyService.removeListener(this) - historyService.load() - historyService.addListener(this) - val needsServerList = historyService.addedServers?.secureInternetServer != null - if (needsServerList) { - refreshServerList() - } else { - refreshInstances() + viewModelScope.launch(Dispatchers.IO) { + try { + historyService.removeListener(this@ServerSelectionViewModel) + historyService.load() + historyService.addListener(this@ServerSelectionViewModel) + + val needsServerList = historyService.addedServers?.secureInternetServer != null + if (needsServerList) { + refreshServerList() + } else { + refreshInstances() + } + } catch (ex: Exception) { + _parentAction.postValue(ParentAction.DisplayError(R.string.error_dialog_title, ex.message ?: ex.toString())) + } } } @@ -100,7 +107,7 @@ class ServerSelectionViewModel @Inject constructor( * Refreshes the current organization, and then the instances afterwards */ private fun refreshServerList() { - connectionState.value = ConnectionState.FetchingServerList + connectionState.postValue(ConnectionState.FetchingServerList) Log.v(TAG, "Fetching server list...") viewModelScope.launch(Dispatchers.IO) { runCatchingCoroutine { diff --git a/app/src/main/java/nl/eduvpn/app/viewmodel/SettingsViewModel.kt b/app/src/main/java/nl/eduvpn/app/viewmodel/SettingsViewModel.kt index caad4adf..46a78911 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/SettingsViewModel.kt @@ -2,14 +2,15 @@ package nl.eduvpn.app.viewmodel import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import nl.eduvpn.app.service.BackendService import nl.eduvpn.app.service.HistoryService -import nl.eduvpn.app.service.PreferencesService import javax.inject.Inject class SettingsViewModel @Inject constructor( private val historyService: HistoryService, - private val preferencesService: PreferencesService, private val backendService: BackendService ) : ViewModel() { @@ -18,6 +19,8 @@ class SettingsViewModel @Inject constructor( val hasAddedServers get() = historyService.addedServers?.hasServers() == true fun removeOrganizationData() { - historyService.removeOrganizationData() + viewModelScope.launch(Dispatchers.IO) { + historyService.removeOrganizationData() + } } } \ No newline at end of file diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 6d37ce8f..c9760fbe 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -191,10 +191,6 @@ VPN connectada Per a canviar entre perfils cal que desconnecteu primer emprant el botó de commutació. OK - La versió de la llista d\'organitzacions és obsoleta. - Problema en descarregar la llista d\'organitzacions - La versió de la llista de servidors és obsoleta. - Problema en descarregar la llista de servidors Renovar Sessió Advertiment de caducitat del certificat VPN Certificat VPN a punt de caducar diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e85dbef3..0c0f95a7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -185,10 +185,6 @@ VPN verbunden Zum wechseln von Profilen bitte erst die Verbindung mit dem Schalter trennen. OK - Organisationsliste veraltet. - Problem beim Herunterladen der Organisationsliste - Serverliste veraltet. - Problem beim Herunterladen der Serverliste Session erneuern VPN-Zertifikat Ablaufwarnung VPN-Zertifikat wird bald ablaufen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f045d031..f7ce200b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -184,8 +184,4 @@ VPN conectada Para cambiar entre perfiles, primero debe desconectarse usando el interruptor. OK - La versión de la lista de organizaciones está desactualizada. - Problema al descargar la lista de organizaciones - La versión de la lista de servidores está desactualizada. - Problema al descargar la lista de servidores diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 66e5c0a7..0128ae6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -197,10 +197,6 @@ VPN connected To switch between profiles, you need to disconnect first using the toggle. OK - Organization list version is outdated. - Problem downloading organization list - Server list version is outdated. - Problem downloading server list Renew Session VPN certificate expired warning VPN certificate about to expire From 471eb148dcc102e158dc6a5a0a826c7c9ccfc874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Zolnai?= Date: Thu, 17 Oct 2024 15:19:17 +0200 Subject: [PATCH 3/3] More crash fixes --- .../app/fragment/ConnectionStatusFragment.kt | 42 ++++++++++++------- .../fragment/OrganizationSelectionFragment.kt | 8 +++- .../nl/eduvpn/app/inject/ApplicationModule.kt | 2 +- .../app/livedata/ConnectionTimeLiveData.kt | 6 +-- .../nl/eduvpn/app/service/BackendService.kt | 5 +-- .../app/service/EduVPNOpenVPNService.java | 12 +++++- .../nl/eduvpn/app/service/HistoryService.kt | 4 +- .../java/nl/eduvpn/app/utils/ErrorDialog.kt | 4 +- .../app/viewmodel/BaseConnectionViewModel.kt | 13 ------ .../viewmodel/ConnectionStatusViewModel.kt | 2 +- .../nl/eduvpn/app/viewmodel/MainViewModel.kt | 12 +++--- .../app/viewmodel/ServerSelectionViewModel.kt | 3 ++ 12 files changed, 65 insertions(+), 48 deletions(-) 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())) } }