Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/2024.1' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
octol committed Nov 28, 2024
2 parents d3bafc5 + 0e7396e commit 0d52c42
Show file tree
Hide file tree
Showing 120 changed files with 1,670 additions and 1,432 deletions.
12 changes: 5 additions & 7 deletions nym-vpn-android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ android {
vectorDrawables {
useSupportLibrary = true
}
buildConfigField(
"String",
Constants.SENTRY_DSN,
"\"${(System.getenv(Constants.SENTRY_DSN) ?: getLocalProperty("sentry.dsn")) ?: ""}\"",
)

buildConfigField("String[]", "LANGUAGES", "new String[]{ ${languageList().joinToString(separator = ", ") { "\"$it\"" }} }")

buildConfigField("String", "COMMIT_HASH", "\"${grgitService.service.get().grgit.head().id}\"")
buildConfigField("Boolean", "IS_SANDBOX", "false")
buildConfigField("Boolean", "IS_PRERELEASE", "false")
proguardFile("fdroid-rules.pro")
}

Expand Down Expand Up @@ -96,6 +94,7 @@ android {
versionNameSuffix = "-pre"
resValue("string", "app_name", "NymVPN - Pre")
resValue("string", "provider", "\"${Constants.APP_NAME}.provider.pre\"")
buildConfigField("Boolean", "IS_PRERELEASE", "true")
}

create(Constants.NIGHTLY) {
Expand Down Expand Up @@ -166,7 +165,6 @@ dependencies {

implementation(project(":nym-vpn-client"))
implementation(project(":logcat-util"))
implementation(project(":localization-util"))
implementation(libs.androidx.lifecycle.process)
coreLibraryDesugaring(libs.com.android.tools.desugar)

Expand Down
5 changes: 3 additions & 2 deletions nym-vpn-android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@
tools:replace="screenOrientation" />
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|keyboardHidden|locale|layoutDirection"
android:exported="true"
android:theme="@style/Theme.NymVPN"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
android:windowSoftInputMode="adjustResize"
android:configChanges="orientation|screenSize|keyboardHidden|locale"
>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import net.nymtech.logcatutil.LogCollect
import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.nymvpn.module.qualifiers.ApplicationScope
import net.nymtech.nymvpn.module.qualifiers.IoDispatcher
import net.nymtech.nymvpn.util.LocaleUtil
import net.nymtech.nymvpn.util.extensions.requestTileServiceStateUpdate
import net.nymtech.nymvpn.util.timber.ReleaseTree
import net.nymtech.vpn.backend.Backend
Expand Down Expand Up @@ -61,6 +62,11 @@ class NymVpn : Application() {
} else {
Timber.plant(ReleaseTree())
}
applicationScope.launch {
settingsRepository.getLocale()?.let {
LocaleUtil.changeLocale(it)
}
}
applicationScope.launch(ioDispatcher) {
logCollect.start()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,9 @@ interface SettingsRepository {

suspend fun getExitGatewayId(): String?

suspend fun getLocale(): String?

suspend fun setLocale(locale: String)

val settingsFlow: Flow<Settings>
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager
private val credentialMode = booleanPreferencesKey("CREDENTIAL_MODE")
private val entryGateway = stringPreferencesKey("ENTRY_GATEWAY_ID")
private val exitGateway = stringPreferencesKey("EXIT_GATEWAY_ID")
private val locale = stringPreferencesKey("LOCALE")

override suspend fun init() {
val firstHop = dataStoreManager.getFromStore(firstHopCountry)
Expand Down Expand Up @@ -169,6 +170,14 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager
return dataStoreManager.getFromStore(exitGateway)
}

override suspend fun getLocale(): String? {
return dataStoreManager.getFromStore(locale)
}

override suspend fun setLocale(locale: String) {
dataStoreManager.saveToDataStore(this.locale, locale)
}

override val settingsFlow: Flow<Settings> =
dataStoreManager.preferencesFlow.map { prefs ->
prefs?.let { pref ->
Expand Down Expand Up @@ -197,6 +206,7 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager
isCredentialMode = pref[credentialMode],
entryGatewayId = pref[entryGateway],
exitGatewayId = pref[exitGateway],
locale = pref[locale],
)
} catch (e: IllegalArgumentException) {
Timber.e(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ data class Settings(
val isCredentialMode: Boolean? = null,
val entryGatewayId: String? = null,
val exitGatewayId: String? = null,
val locale: String? = null,
) {
companion object {
const val AUTO_START_DEFAULT = false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package net.nymtech.nymvpn.service.tunnel

import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.BackendMessage
import net.nymtech.vpn.model.Statistics
import net.nymtech.vpn.model.BackendEvent
import nym_vpn_lib.EntryPoint
import nym_vpn_lib.ExitPoint

Expand All @@ -12,19 +11,14 @@ class NymTunnel(
override var mode: Tunnel.Mode,
override var environment: Tunnel.Environment,
val stateChange: (newState: Tunnel.State) -> Unit,
val statChange: (stats: Statistics) -> Unit,
val backendMessage: (message: BackendMessage) -> Unit,
val backendEvent: (message: BackendEvent) -> Unit,
override var credentialMode: Boolean?,
) : Tunnel {
override fun onStateChange(newState: Tunnel.State) {
stateChange(newState)
}

override fun onStatisticChange(stats: Statistics) {
statChange(stats)
}

override fun onBackendMessage(message: BackendMessage) {
backendMessage(message)
override fun onBackendEvent(event: BackendEvent) {
backendEvent(event)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@ import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.nymvpn.module.qualifiers.ApplicationScope
import net.nymtech.nymvpn.module.qualifiers.IoDispatcher
import net.nymtech.nymvpn.service.notification.NotificationService
import net.nymtech.nymvpn.service.tunnel.model.BackendUiEvent
import net.nymtech.nymvpn.service.tunnel.model.MixnetConnectionState
import net.nymtech.nymvpn.util.extensions.requestTileServiceStateUpdate
import net.nymtech.nymvpn.util.extensions.toMB
import net.nymtech.nymvpn.util.extensions.toUserMessage
import net.nymtech.vpn.backend.Backend
import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.BackendMessage
import net.nymtech.vpn.model.Statistics
import net.nymtech.vpn.model.BackendEvent
import net.nymtech.vpn.util.exceptions.NymVpnInitializeException
import nym_vpn_lib.AccountLinks
import nym_vpn_lib.AccountStateSummary
import nym_vpn_lib.BandwidthEvent
import nym_vpn_lib.ConnectionData
import nym_vpn_lib.ConnectionEvent
import nym_vpn_lib.EntryPoint
import nym_vpn_lib.ErrorStateReason
import nym_vpn_lib.ExitPoint
import nym_vpn_lib.MixnetEvent
import nym_vpn_lib.TunnelState
import nym_vpn_lib.VpnException
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Provider
Expand All @@ -49,7 +56,6 @@ class NymTunnelManager @Inject constructor(
_state.update {
it.copy(
isMnemonicStored = isMnemonicStored,
accountLinks = if (isMnemonicStored) getAccountLinks() else null,
)
}
}.stateIn(applicationScope.plus(ioDispatcher), SharingStarted.Eagerly, TunnelManagerState())
Expand All @@ -66,14 +72,15 @@ class NymTunnelManager @Inject constructor(

override suspend fun start(fromBackground: Boolean) {
runCatching {
// clear any error states
emitBackendUiEvent(null)
val tunnel = NymTunnel(
entryPoint = getEntryPoint(),
exitPoint = getExitPoint(),
mode = settingsRepository.getVpnMode(),
environment = settingsRepository.getEnvironment(),
statChange = ::emitStats,
stateChange = ::onStateChange,
backendMessage = ::onBackendMessage,
backendEvent = ::onBackendEvent,
credentialMode = settingsRepository.isCredentialMode(),
)
backend.get().start(tunnel, fromBackground)
Expand Down Expand Up @@ -118,6 +125,7 @@ class NymTunnelManager @Inject constructor(
override suspend fun storeMnemonic(mnemonic: String) {
backend.get().storeMnemonic(mnemonic)
emitMnemonicStored(true)
refreshAccountLinks()
}

override suspend fun isMnemonicStored(): Boolean {
Expand All @@ -127,6 +135,7 @@ class NymTunnelManager @Inject constructor(
override suspend fun removeMnemonic() {
backend.get().removeMnemonic()
emitMnemonicStored(false)
refreshAccountLinks()
}

override suspend fun getAccountSummary(): AccountStateSummary {
Expand All @@ -141,37 +150,65 @@ class NymTunnelManager @Inject constructor(
}
}

override suspend fun refreshAccountLinks() {
val accountLinks = getAccountLinks()
_state.update {
it.copy(accountLinks = accountLinks)
}
}

private fun emitMnemonicStored(stored: Boolean) {
_state.update {
it.copy(isMnemonicStored = stored)
}
}

private fun onBackendMessage(backendMessage: BackendMessage) {
launchBackendNotification(backendMessage)
emitMessage(backendMessage)
// TODO For now, we'll stop tunnel on errors
if (backendMessage is BackendMessage.Failure) {
Timber.d("Shutting tunnel down on fatal error")
applicationScope.launch(ioDispatcher) {
backend.get().stop()
}
private fun emitBackendUiEvent(backendEvent: BackendUiEvent?) {
_state.update {
it.copy(backendUiEvent = backendEvent)
}
}

private fun emitMessage(backendMessage: BackendMessage) {
private fun emitConnectionData(connectionData: ConnectionData?) {
_state.update {
it.copy(
backendMessage = backendMessage,
)
it.copy(connectionData = connectionData)
}
}

private fun emitStats(statistics: Statistics) {
private fun emitMixnetConnectionEvent(connectionEvent: ConnectionEvent) {
_state.update {
it.copy(
tunnelStatistics = statistics,
)
it.copy(mixnetConnectionState = it.mixnetConnectionState?.onEvent(connectionEvent) ?: MixnetConnectionState().onEvent(connectionEvent))
}
}

private fun onBackendEvent(backendEvent: BackendEvent) {
when (backendEvent) {
is BackendEvent.Mixnet -> when (val event = backendEvent.event) {
is MixnetEvent.Bandwidth -> {
emitBackendUiEvent(BackendUiEvent.BandwidthAlert(event.v1))
launchBandwidthNotification(event.v1)
}
is MixnetEvent.Connection -> emitMixnetConnectionEvent(event.v1)
}

is BackendEvent.StartFailure -> {
emitBackendUiEvent(BackendUiEvent.StartFailure(backendEvent.exception))
launchStartFailureNotification(backendEvent.exception)
}
is BackendEvent.Tunnel -> when (val state = backendEvent.state) {
is TunnelState.Connected -> emitConnectionData(state.connectionData)
is TunnelState.Connecting -> emitConnectionData(state.connectionData)
is TunnelState.Disconnecting -> Timber.d("After disconnect status: ${state.afterDisconnect.name}")
is TunnelState.Error -> {
Timber.d("Shutting tunnel down on fatal error")
emitBackendUiEvent(BackendUiEvent.Failure(state.v1))
launchBackendFailureNotification(state.v1)
applicationScope.launch(ioDispatcher) {
backend.get().stop()
}
}
else -> Unit
}
}
}

Expand All @@ -198,31 +235,30 @@ class NymTunnelManager @Inject constructor(
}
}

private fun launchBackendNotification(backendMessage: BackendMessage) {
when (backendMessage) {
is BackendMessage.Failure -> {
notificationService.showNotification(
title = context.getString(R.string.connection_failed),
description = backendMessage.reason.toUserMessage(context),
)
}
is BackendMessage.BandwidthAlert -> {
when (val alert = backendMessage.status) {
BandwidthEvent.NoBandwidth -> notificationService.showNotification(
title = context.getString(R.string.bandwidth_alert),
description = context.getString(R.string.no_bandwidth),
)
is BandwidthEvent.RemainingBandwidth -> notificationService.showNotification(
title = context.getString(R.string.bandwidth_alert),
description = context.getString(R.string.low_bandwidth) + " ${alert.v1.toMB()} MB",
)
}
}
BackendMessage.None -> Unit
is BackendMessage.StartFailure -> notificationService.showNotification(
title = context.getString(R.string.connection_failed),
description = backendMessage.exception.toUserMessage(context),
private fun launchBandwidthNotification(bandwidthEvent: BandwidthEvent) {
when (bandwidthEvent) {
BandwidthEvent.NoBandwidth -> notificationService.showNotification(
title = context.getString(R.string.bandwidth_alert),
description = context.getString(R.string.no_bandwidth),
)
is BandwidthEvent.RemainingBandwidth -> notificationService.showNotification(
title = context.getString(R.string.bandwidth_alert),
description = context.getString(R.string.low_bandwidth) + " ${bandwidthEvent.v1.toMB()} MB",
)
}
}

private fun launchStartFailureNotification(exception: VpnException) {
notificationService.showNotification(
title = context.getString(R.string.connection_failed),
description = exception.toUserMessage(context),
)
}

private fun launchBackendFailureNotification(reason: ErrorStateReason) {
notificationService.showNotification(
title = context.getString(R.string.connection_failed),
description = reason.toUserMessage(context),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import nym_vpn_lib.AccountStateSummary
interface TunnelManager {
suspend fun stop()
suspend fun start(fromBackground: Boolean = true)
suspend fun storeMnemonic(credential: String)
suspend fun storeMnemonic(mnemonic: String)
suspend fun isMnemonicStored(): Boolean
suspend fun removeMnemonic()
suspend fun getAccountSummary(): AccountStateSummary
suspend fun getAccountLinks(): AccountLinks?
suspend fun refreshAccountLinks()
val stateFlow: Flow<TunnelManagerState>
fun getState(): Tunnel.State
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package net.nymtech.nymvpn.service.tunnel

import net.nymtech.nymvpn.service.tunnel.model.BackendUiEvent
import net.nymtech.nymvpn.service.tunnel.model.MixnetConnectionState
import net.nymtech.vpn.backend.Tunnel
import net.nymtech.vpn.model.BackendMessage
import net.nymtech.vpn.model.Statistics
import nym_vpn_lib.AccountLinks
import nym_vpn_lib.ConnectionData

data class TunnelManagerState(
val tunnelState: Tunnel.State = Tunnel.State.Down,
val tunnelStatistics: Statistics = Statistics(),
val backendMessage: BackendMessage = BackendMessage.None,
val backendUiEvent: BackendUiEvent? = null,
val connectionData: ConnectionData? = null,
val mixnetConnectionState: MixnetConnectionState? = null,
val isMnemonicStored: Boolean = false,
val accountLinks: AccountLinks? = null,
)
Loading

0 comments on commit 0d52c42

Please sign in to comment.