From 1a83d92b09153111f9362fb21af88548a472b4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Fri, 4 Oct 2024 15:40:44 +0200 Subject: [PATCH 01/11] we need a C++ compiler --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e25b39d..cf59b7b6 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,8 @@ $ sudo dnf -y install \ ncurses-compat-libs \ ninja-build \ cmake \ - pv + pv \ + gcc-c++ ``` ## Key Store From d648b061e5b5f7a419edbf269ffd8d78451c447e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Mon, 7 Oct 2024 16:02:56 +0200 Subject: [PATCH 02/11] point to codeberg.org/eduVPN/wireguard-android --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index c09634ad..29852fcc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/eduvpn/eduvpn-common [submodule "wireguard"] path = wireguard - url = https://codeberg.org/fkooman/wireguard-android + url = https://codeberg.org/eduVPN/wireguard-android From 9b7e03260caba22466ccdd4f06f6d66c7ee8f5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Mon, 7 Oct 2024 21:02:47 +0200 Subject: [PATCH 03/11] small README updates --- README.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cf59b7b6..90c91754 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ $ git clone --recurse-submodules https://codeberg.org/eduVPN/android # Download the app +## Stores + [Get it on F-Droid](https://f-droid.org/packages/nl.eduvpn.app/) @@ -17,6 +19,11 @@ $ git clone --recurse-submodules https://codeberg.org/eduVPN/android alt="Get it on Google Play" height="80">](https://play.google.com/store/apps/details?id=nl.eduvpn.app) +## APKs + +* [APKs for eduVPN](https://app.eduvpn.org/#android) +* [APKs for Let's Connect!](https://app.letsconnect-vpn.org/#android) + # Running with Android Studio First install the `swig` and `go` packages with your operating system package manager. @@ -47,10 +54,10 @@ Anything lower was not tested, but 1GB of memory definitely does not work. The builder will spew a lot of backtraces/warnings, but does seem to complete eventually. -We last tested this on 2023-10-20 with Fedora 38. +We last tested this on 2024-10-07 with Fedora 40. We removed the Debian instructions as building on Debian no longer works on -Debian 11 (as of 2023-03-06) and I have no desire to investigate fixing this. +Debian 11 (as of 2023-03-06). ## Dependencies @@ -76,15 +83,17 @@ $ sudo dnf -y install \ Generate a key store for signing the Android application: - $ keytool \ - -genkey \ - -keystore ${HOME}/android.jks \ - -keyalg RSA \ - -keysize 4096 \ - -sigalg SHA256withRSA \ - -dname "CN=eduVPN for Android" \ - -validity 10000 \ - -alias eduVPN +```bash +$ keytool \ + -genkey \ + -keystore ${HOME}/android.jks \ + -keyalg RSA \ + -keysize 4096 \ + -sigalg SHA256withRSA \ + -dname "CN=eduVPN for Android" \ + -validity 10000 \ + -alias eduVPN +``` Additional documentation [here](https://developer.android.com/studio/publish/app-signing#signing-manually). From 08efc73c5b13c206f3550df16a18b2d8a95bc61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Thu, 17 Oct 2024 09:19:37 +0200 Subject: [PATCH 04/11] update wireguard dependency, point to "eduVPN" branch --- wireguard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wireguard b/wireguard index 2055ab96..6d7f64f5 160000 --- a/wireguard +++ b/wireguard @@ -1 +1 @@ -Subproject commit 2055ab9671aa6d8eeee0567f45449eb3237f8b8f +Subproject commit 6d7f64f5e044ae28816efe8fa3a48130fdd8618a From 7bb7ea59373f439899bf64f45fddc61dbf2382c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Thu, 17 Oct 2024 09:53:14 +0200 Subject: [PATCH 05/11] update WireGuard --- wireguard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wireguard b/wireguard index 6d7f64f5..d9ec7c1d 160000 --- a/wireguard +++ b/wireguard @@ -1 +1 @@ -Subproject commit 6d7f64f5e044ae28816efe8fa3a48130fdd8618a +Subproject commit d9ec7c1d476305ee08b593305d64821caee53c49 From 89ae2ebe28e02c8b96a981b6cf8e2d480b8594be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Thu, 17 Oct 2024 12:41:28 +0200 Subject: [PATCH 06/11] update eduvpn-common --- common/libs/eduvpn-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From c07bf04a469fed1aea972c2380c3d9e929f5fe21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Thu, 17 Oct 2024 12:56:31 +0200 Subject: [PATCH 07/11] point to codeberg fork of ics-openvpn --- .gitmodules | 2 +- ics-openvpn | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 29852fcc..7093e588 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "ics-openvpn"] path = ics-openvpn - url = https://github.com/eduvpn/ics-openvpn + url = https://codeberg.org/eduVPN/ics-openvpn [submodule "common/libs/eduvpn-common"] path = common/libs/eduvpn-common url = https://github.com/eduvpn/eduvpn-common diff --git a/ics-openvpn b/ics-openvpn index 3520bde9..a7461c5d 160000 --- a/ics-openvpn +++ b/ics-openvpn @@ -1 +1 @@ -Subproject commit 3520bde975dede6d3cd5a65278312b0ecf1014c1 +Subproject commit a7461c5dffab3616c94476aac4dc95dad0d2b909 From 41147c7c72580b57077a7c741af85ac7fc2f8226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Thu, 17 Oct 2024 13:12:36 +0200 Subject: [PATCH 08/11] update to Gradle 8.9 wrapper --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48c0a02c..19cfad96 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.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5e842271f860b050810c7f67bbb401de43e846b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Zolnai?= Date: Thu, 17 Oct 2024 22:46:21 +0200 Subject: [PATCH 09/11] * Update, WG, ics-openvpn, common submodules * Move backend methods to background threads * More crash fixes --- app/src/main/AndroidManifest.xml | 21 +-- .../java/nl/eduvpn/app/OpenVpnLogsActivity.kt | 41 ++++++ .../app/fragment/ConnectionStatusFragment.kt | 50 +++++-- .../fragment/OrganizationSelectionFragment.kt | 8 +- .../app/fragment/ProfileSelectionFragment.kt | 2 +- .../eduvpn/app/fragment/SettingsFragment.kt | 3 +- .../nl/eduvpn/app/inject/ApplicationModule.kt | 2 +- .../nl/eduvpn/app/inject/EduVPNComponent.kt | 2 + .../app/livedata/ConnectionTimeLiveData.kt | 6 +- .../nl/eduvpn/app/service/BackendService.kt | 3 +- .../app/service/EduVPNOpenVPNService.java | 12 +- .../nl/eduvpn/app/service/HistoryService.java | 135 ------------------ .../nl/eduvpn/app/service/HistoryService.kt | 121 ++++++++++++++++ .../java/nl/eduvpn/app/utils/ErrorDialog.kt | 4 +- .../app/viewmodel/BaseConnectionViewModel.kt | 17 +-- .../viewmodel/ConnectionStatusViewModel.kt | 10 +- .../nl/eduvpn/app/viewmodel/MainViewModel.kt | 45 +++--- .../app/viewmodel/ServerSelectionViewModel.kt | 28 ++-- .../eduvpn/app/viewmodel/SettingsViewModel.kt | 9 +- app/src/main/res/layout/activity_api_logs.xml | 2 +- .../main/res/layout/activity_openvpn_logs.xml | 37 +++++ .../res/layout/fragment_connection_status.xml | 8 +- 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 - gradle/wrapper/gradle-wrapper.properties | 2 +- 27 files changed, 334 insertions(+), 250 deletions(-) create mode 100644 app/src/main/java/nl/eduvpn/app/OpenVpnLogsActivity.kt 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 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/ConnectionStatusFragment.kt b/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt index fee4747b..f1592f7e 100644 --- a/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt +++ b/app/src/main/java/nl/eduvpn/app/fragment/ConnectionStatusFragment.kt @@ -23,6 +23,7 @@ import android.view.View import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.core.view.postDelayed +import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer import androidx.lifecycle.asLiveData @@ -45,9 +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. @@ -62,17 +66,21 @@ class ConnectionStatusFragment : BaseFragment() private val viewModel by viewModels { viewModelFactory } + private val mainViewModel by activityViewModels { viewModelFactory } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) EduVPNApplication.get(view.context).component().inject(this) binding.viewModel = viewModel binding.isTcp = viewModel.isCurrentProtocolUsingTcp() + 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 @@ -206,6 +214,9 @@ class ConnectionStatusFragment : BaseFragment() updateCertExpiryObserver?.let { obs -> viewModel.timer.removeObserver(obs) } } } + mainViewModel.failoverResult.observe(viewLifecycleOwner) { + binding.failoverNeeded = it + } viewModel.timer.observe(viewLifecycleOwner, updateCertExpiryObserver) viewModel.vpnStatus.observe(viewLifecycleOwner) { status -> binding.connectionStatus.setText(VPNConnectionService.vpnStatusToStringID(status)) @@ -292,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/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/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/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/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/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 38f32b51..908f2baa 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 @@ -233,7 +233,6 @@ class BackendService( return true } - @kotlin.jvm.Throws(CommonException::class, UnknownFormatException::class) fun getAddedServers(): AddedServers { val dataErrorTuple = goBackend.addedServers if (dataErrorTuple.isError) { 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.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..d0234083 --- /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) + 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) + 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/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 1db62c3e..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,23 +140,14 @@ 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) } 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..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( @@ -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 e27b3f03..5c55748d 100644 --- a/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt +++ b/app/src/main/java/nl/eduvpn/app/viewmodel/MainViewModel.kt @@ -69,6 +69,9 @@ class MainViewModel @Inject constructor( val mainParentAction = _mainParentAction.toSingleEvent() val proxyGuardEnabled: Boolean get() = preferencesService.getCurrentProtocol() == Protocol.WireGuardWithTCP.nativeValue + private val _failoverResult = MutableLiveData(false) + val failoverResult = _failoverResult.toSingleEvent() + init { backendService.register( @@ -94,12 +97,24 @@ class MainViewModel @Inject constructor( _mainParentAction.postValue(MainParentAction.OnProxyGuardReady) } ) - historyService.load() + 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() } @@ -150,7 +165,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) { @@ -159,21 +174,7 @@ class MainViewModel @Inject constructor( // Waits a bit so that the network interface has been surely set up delay(1_000L) backendService.startFailOver(service) { - // Failover needed, request a new profile with TCP enforced. - preferencesService.getCurrentInstance()?.let { currentInstance -> - viewModelScope.launch { - // Disconnect first, otherwise we don't have any internet :) - service.disconnect() - // Wait a bit for the disconnection to finish - delay(500L) - // Fetch a new profile, now with TCP forced - try { - backendService.getConfig(currentInstance, preferTcp = true) - } catch (ex: Exception) { - Log.w(TAG, "Could not fetch new config!", ex) - } - } - } + _failoverResult.postValue(true) } } catch (ex: CommonException) { // These are just warnings, so we log them, but don't display to the user @@ -216,7 +217,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..e2133d9b 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,24 @@ 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) { + 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() + } else { + 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())) + } } } @@ -100,7 +110,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/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/app/src/main/res/layout/fragment_connection_status.xml b/app/src/main/res/layout/fragment_connection_status.xml index 3b10d356..61277b3b 100644 --- a/app/src/main/res/layout/fragment_connection_status.xml +++ b/app/src/main/res/layout/fragment_connection_status.xml @@ -38,6 +38,10 @@ name="isTcp" type="boolean" /> + + @@ -209,7 +213,7 @@ android:layout_marginTop="16dp" android:textColor="@color/textColor" android:textSize="14sp" - android:visibility="@{isTcp ? View.GONE : View.VISIBLE}" + android:visibility="@{isTcp || !failoverNeeded ? View.GONE : View.VISIBLE}" android:text="@string/connection_status_connectivity_problems" android:layout_height="wrap_content"/> @@ -218,7 +222,7 @@ android:id="@+id/reconnect_tcp_button" android:layout_width="wrap_content" android:layout_marginTop="8dp" - android:visibility="@{isTcp ? View.GONE : View.VISIBLE}" + android:visibility="@{isTcp || !failoverNeeded ? View.GONE : View.VISIBLE}" android:text="@string/connection_status_reconnect_with_tcp" android:layout_height="wrap_content"/> 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 19cfad96..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.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 43b216f30c2df225316876924da5d8844ffd7707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Fri, 18 Oct 2024 15:25:46 +0200 Subject: [PATCH 10/11] update README --- CHANGES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5e4d6539..e83a06bf 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changelog +## 3.3.4 (...) +* update WireGuard, OpenVPN and "common" sub-modules +* Move backend methods to background threads +* More crash fixes +* small README updates + ## 3.3.3 (2024-10-04) - embed WireGuard for Android - fix crash when reconnecting From fc1416b142f33224d0a5837889b0061ce48aa854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Kooman?= Date: Fri, 18 Oct 2024 15:27:41 +0200 Subject: [PATCH 11/11] prepare for release --- CHANGES.md | 8 ++++---- app/build.gradle | 4 ++-- build_app_git.sh | 2 +- build_app_tar.sh | 2 +- create_release_tar.sh | 2 +- fastlane/metadata/android/en-US/changelogs/31.txt | 4 ++++ 6 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/31.txt diff --git a/CHANGES.md b/CHANGES.md index e83a06bf..a8bdd19c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,10 @@ # Changelog ## 3.3.4 (...) -* update WireGuard, OpenVPN and "common" sub-modules -* Move backend methods to background threads -* More crash fixes -* small README updates +- update WireGuard, OpenVPN and "common" sub-modules +- Move backend methods to background threads +- More crash fixes +- small README updates ## 3.3.3 (2024-10-04) - embed WireGuard for Android diff --git a/app/build.gradle b/app/build.gradle index cf1d557b..964dbd86 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,8 +15,8 @@ android { applicationId "nl.eduvpn.app" minSdkVersion 21 targetSdkVersion 34 - versionCode 30 - versionName "3.3.3" + versionCode 31 + versionName "3.3.4" ndkVersion "26.1.10909125" vectorDrawables.useSupportLibrary = true diff --git a/build_app_git.sh b/build_app_git.sh index d315efea..f0e895c7 100755 --- a/build_app_git.sh +++ b/build_app_git.sh @@ -8,7 +8,7 @@ SDK_DIR=${HOME}/android-sdk KEY_STORE=${HOME}/android.jks GIT_REPO=https://codeberg.org/eduVPN/android -#GIT_TAG=3.3.3 +#GIT_TAG=3.3.4 GIT_TAG=master PROJECT_DIR=${HOME}/Projects diff --git a/build_app_tar.sh b/build_app_tar.sh index de42a04c..4c049bd3 100755 --- a/build_app_tar.sh +++ b/build_app_tar.sh @@ -7,7 +7,7 @@ SDK_DIR=${HOME}/android-sdk KEY_STORE=${HOME}/android.jks -V=3.3.3 +V=3.3.4 DOWNLOAD_URL=https://codeberg.org/eduVPN/android/releases/download/${V}/eduvpn-android-${V}.tar.xz PROJECT_DIR=${HOME}/Projects diff --git a/create_release_tar.sh b/create_release_tar.sh index c7badd92..2858c62f 100755 --- a/create_release_tar.sh +++ b/create_release_tar.sh @@ -1,7 +1,7 @@ #!/bin/sh GIT_REPO=https://codeberg.org/eduVPN/android -GIT_TAG=3.3.3 +GIT_TAG=3.3.4 #GIT_TAG=master ############################################################################### diff --git a/fastlane/metadata/android/en-US/changelogs/31.txt b/fastlane/metadata/android/en-US/changelogs/31.txt new file mode 100644 index 00000000..787664e5 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/31.txt @@ -0,0 +1,4 @@ +- update WireGuard, OpenVPN and "common" sub-modules +- Move backend methods to background threads +- More crash fixes +- small README updates