Skip to content
This repository has been archived by the owner on Jul 11, 2024. It is now read-only.

Commit

Permalink
uniffi refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
zaneschepke committed Mar 31, 2024
1 parent 45b9640 commit d509c44
Show file tree
Hide file tree
Showing 37 changed files with 567 additions and 455 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ dependencies {
implementation(libs.lifecycle.runtime.compose)
implementation(libs.kotlinx.serialization)
implementation(libs.androidx.window)
implementation(libs.androidx.lifecycle.service)

// logging
implementation(libs.timber)
Expand Down
23 changes: 11 additions & 12 deletions app/src/main/java/net/nymtech/nymvpn/data/GatewayRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@ package net.nymtech.nymvpn.data

import kotlinx.coroutines.flow.Flow
import net.nymtech.nymvpn.data.model.Gateways
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.HopCountries
import net.nymtech.vpn.model.Country

interface GatewayRepository {
suspend fun getFirstHopCountry() : Hop.Country
suspend fun setFirstHopCountry(country: Hop.Country)
suspend fun getFirstHopCountry() : Country
suspend fun setFirstHopCountry(country: Country)

suspend fun getLowLatencyCountry() : Hop.Country
suspend fun setLowLatencyCountry(country: Hop.Country)
suspend fun getLowLatencyCountry() : Country
suspend fun setLowLatencyCountry(country: Country)

suspend fun getLastHopCountry() : Hop.Country
suspend fun setLastHopCountry(country: Hop.Country)
suspend fun getLastHopCountry() : Country
suspend fun setLastHopCountry(country: Country)

suspend fun setEntryCountries(countries: HopCountries)
suspend fun getEntryCountries() : HopCountries
suspend fun setEntryCountries(countries: Set<Country>)
suspend fun getEntryCountries() : Set<Country>

suspend fun setExitCountries(countries: HopCountries)
suspend fun getExitCountries() : HopCountries
suspend fun setExitCountries(countries: Set<Country>)
suspend fun getExitCountries() : Set<Country>

val gatewayFlow : Flow<Gateways>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,64 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import net.nymtech.nymvpn.data.GatewayRepository
import net.nymtech.nymvpn.data.model.Gateways
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.HopCountries
import net.nymtech.vpn.model.Country
import timber.log.Timber

class DataStoreGatewayRepository(private val dataStoreManager: DataStoreManager) : GatewayRepository {
override suspend fun getFirstHopCountry(): Hop.Country {
override suspend fun getFirstHopCountry(): Country {
val country = dataStoreManager.getFromStore(DataStoreManager.FIRST_HOP_COUNTRY)
return Hop.Country.from(country)
return Country.from(country)
}

override suspend fun setFirstHopCountry(country: Hop.Country) {
override suspend fun setFirstHopCountry(country: Country) {
dataStoreManager.saveToDataStore(DataStoreManager.FIRST_HOP_COUNTRY, country.toString())
}

override suspend fun getLowLatencyCountry(): Hop.Country {
override suspend fun getLowLatencyCountry(): Country {
val country = dataStoreManager.getFromStore(DataStoreManager.LOW_LATENCY_COUNTRY)
return Hop.Country.from(country)
return Country.from(country)
}

override suspend fun setLowLatencyCountry(country: Hop.Country) {
override suspend fun setLowLatencyCountry(country: Country) {
dataStoreManager.saveToDataStore(DataStoreManager.LOW_LATENCY_COUNTRY, country.toString())
}

override suspend fun getLastHopCountry(): Hop.Country {
override suspend fun getLastHopCountry(): Country {
val country = dataStoreManager.getFromStore(DataStoreManager.LAST_HOP_COUNTRY)
return Hop.Country.from(country)
return Country.from(country)
}

override suspend fun setLastHopCountry(country: Hop.Country) {
override suspend fun setLastHopCountry(country: Country) {
dataStoreManager.saveToDataStore(DataStoreManager.LAST_HOP_COUNTRY, country.toString())
}

override suspend fun setEntryCountries(countries: HopCountries) {
override suspend fun setEntryCountries(countries: Set<Country>) {
dataStoreManager.saveToDataStore(DataStoreManager.ENTRY_COUNTRIES, countries.toString())
}

override suspend fun getEntryCountries(): HopCountries {
override suspend fun getEntryCountries(): Set<Country> {
val countries = dataStoreManager.getFromStore(DataStoreManager.ENTRY_COUNTRIES)
return Hop.Country.fromCollectionString(countries)
return Country.fromCollectionString(countries)
}

override suspend fun setExitCountries(countries: HopCountries) {
override suspend fun setExitCountries(countries: Set<Country>) {
dataStoreManager.saveToDataStore(DataStoreManager.EXIT_COUNTRIES, countries.toString())
}

override suspend fun getExitCountries(): HopCountries {
override suspend fun getExitCountries(): Set<Country> {
val countries = dataStoreManager.getFromStore(DataStoreManager.EXIT_COUNTRIES)
return Hop.Country.fromCollectionString(countries)
return Country.fromCollectionString(countries)
}

override val gatewayFlow: Flow<Gateways> = dataStoreManager.preferencesFlow.map { prefs ->
prefs?.let { pref ->
try{
Gateways(
firstHopCountry = Hop.Country.from(pref[DataStoreManager.FIRST_HOP_COUNTRY]),
lastHopCountry = Hop.Country.from(pref[DataStoreManager.LAST_HOP_COUNTRY]),
lowLatencyCountry = Hop.Country.from(pref[DataStoreManager.LOW_LATENCY_COUNTRY]),
exitCountries = Hop.Country.fromCollectionString(pref[DataStoreManager.EXIT_COUNTRIES]),
entryCountries = Hop.Country.fromCollectionString(pref[DataStoreManager.ENTRY_COUNTRIES])
firstHopCountry = Country.from(pref[DataStoreManager.FIRST_HOP_COUNTRY]),
lastHopCountry = Country.from(pref[DataStoreManager.LAST_HOP_COUNTRY]),
lowLatencyCountry = Country.from(pref[DataStoreManager.LOW_LATENCY_COUNTRY]),
exitCountries = Country.fromCollectionString(pref[DataStoreManager.EXIT_COUNTRIES]),
entryCountries = Country.fromCollectionString(pref[DataStoreManager.ENTRY_COUNTRIES])
)
} catch (e : IllegalArgumentException) {
Timber.e(e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.Country
import timber.log.Timber
import java.io.IOException

Expand Down Expand Up @@ -40,8 +40,8 @@ class DataStoreManager(private val context: Context) {

suspend fun init() {
context.dataStore.edit {
if(it[FIRST_HOP_COUNTRY] == null) it[FIRST_HOP_COUNTRY] = Hop.Country(isDefault = true).toString()
if(it[LAST_HOP_COUNTRY] == null) it[LAST_HOP_COUNTRY] = Hop.Country(isDefault = true).toString()
if(it[FIRST_HOP_COUNTRY] == null) it[FIRST_HOP_COUNTRY] = Country(isDefault = true).toString()
if(it[LAST_HOP_COUNTRY] == null) it[LAST_HOP_COUNTRY] = Country(isDefault = true).toString()
}
}

Expand Down
14 changes: 6 additions & 8 deletions app/src/main/java/net/nymtech/nymvpn/data/model/Gateways.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package net.nymtech.nymvpn.data.model

import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.HopCountries

import net.nymtech.vpn.model.Country
data class Gateways(
val firstHopCountry: Hop.Country = Hop.Country(),
val lastHopCountry: Hop.Country = Hop.Country(),
val lowLatencyCountry: Hop.Country = Hop.Country(),
val entryCountries: HopCountries = emptySet(),
val exitCountries: HopCountries = emptySet()
val firstHopCountry: Country = Country(),
val lastHopCountry: Country = Country(),
val lowLatencyCountry: Country = Country(),
val entryCountries: Set<Country> = emptySet(),
val exitCountries: Set<Country> = emptySet()
)
24 changes: 13 additions & 11 deletions app/src/main/java/net/nymtech/nymvpn/receiver/BootReceiver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,30 @@ import android.content.Context
import android.content.Intent
import dagger.hilt.android.AndroidEntryPoint
import net.nymtech.nymvpn.NymVpn
import net.nymtech.nymvpn.data.datastore.DataStoreManager
import net.nymtech.nymvpn.data.GatewayRepository
import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.nymvpn.util.goAsync
import net.nymtech.vpn.NymVpnClient
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.VpnMode
import javax.inject.Inject

@AndroidEntryPoint
class BootReceiver : BroadcastReceiver() {

@Inject lateinit var dataStoreManager: DataStoreManager
@Inject lateinit var gatewayRepository: GatewayRepository

@Inject lateinit var settingsRepository: SettingsRepository

override fun onReceive(context: Context?, intent: Intent?) = goAsync {
if (Intent.ACTION_BOOT_COMPLETED != intent?.action) return@goAsync
val autoStart = dataStoreManager.getFromStore(DataStoreManager.AUTO_START)
if (autoStart == true) {
val firstHopCountry = dataStoreManager.getFromStore(DataStoreManager.FIRST_HOP_COUNTRY)
val lastHopCountry = dataStoreManager.getFromStore(DataStoreManager.LAST_HOP_COUNTRY)
val mode = dataStoreManager.getFromStore(DataStoreManager.VPN_MODE)
if (settingsRepository.isAutoStartEnabled()) {
val entryCountry = gatewayRepository.getFirstHopCountry()
val exitCountry = gatewayRepository.getLastHopCountry()
val mode = settingsRepository.getVpnMode()
context?.let { context ->
NymVpnClient.connectForeground(context, Hop.Country.from(firstHopCountry), Hop.Country.from(lastHopCountry),
VpnMode.from(mode))
val entry = entryCountry.toEntryPoint()
val exit = exitCountry.toExitPoint()
NymVpnClient.configure(entry,exit,mode)
NymVpnClient.start(context)
NymVpn.requestTileServiceStateUpdate(context)
}
}
Expand Down
37 changes: 23 additions & 14 deletions app/src/main/java/net/nymtech/nymvpn/service/AlwaysOnVpnService.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
package net.nymtech.nymvpn.service

import android.app.Service
import android.content.Intent
import android.os.IBinder
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import net.nymtech.nymvpn.NymVpn
import net.nymtech.nymvpn.data.datastore.DataStoreManager
import net.nymtech.nymvpn.data.GatewayRepository
import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.vpn.NymVpnClient
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.VpnMode
import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
class AlwaysOnVpnService : Service() {
class AlwaysOnVpnService : LifecycleService() {

@Inject
lateinit var dataStoreManager: DataStoreManager
override fun onBind(intent: Intent?): IBinder? {
@Inject lateinit var gatewayRepository: GatewayRepository

@Inject lateinit var settingsRepository: SettingsRepository

override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
// We don't provide binding, so return null
return null
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null || intent.component == null || intent.component!!.packageName != packageName) {
Timber.i("Always-on VPN requested start")
val firstHopCountry = dataStoreManager.getFromStoreBlocking(DataStoreManager.FIRST_HOP_COUNTRY)
val lastHopCountry = dataStoreManager.getFromStoreBlocking(DataStoreManager.LAST_HOP_COUNTRY)
val mode = dataStoreManager.getFromStoreBlocking(DataStoreManager.VPN_MODE)
NymVpn.requestTileServiceStateUpdate(this)
NymVpnClient.connectForeground(this, Hop.Country.from(firstHopCountry), Hop.Country.from(lastHopCountry),
VpnMode.from(mode))
lifecycleScope.launch {
val entryCountry = gatewayRepository.getFirstHopCountry()
val exitCountry = gatewayRepository.getLastHopCountry()
val mode = settingsRepository.getVpnMode()
val entry = entryCountry.toEntryPoint()
val exit = exitCountry.toExitPoint()
NymVpnClient.configure(entry,exit,mode)
NymVpnClient.start(this@AlwaysOnVpnService)
NymVpn.requestTileServiceStateUpdate(this@AlwaysOnVpnService)
}
START_STICKY
} else {
START_NOT_STICKY
Expand Down
33 changes: 20 additions & 13 deletions app/src/main/java/net/nymtech/nymvpn/service/tile/VpnQuickTile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import net.nymtech.nymvpn.NymVpn
import net.nymtech.nymvpn.R
import net.nymtech.nymvpn.data.datastore.DataStoreManager
import net.nymtech.nymvpn.data.GatewayRepository
import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.vpn.NymVpnClient
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.VpnMode
import net.nymtech.vpn.model.VpnState
import timber.log.Timber
Expand All @@ -21,7 +22,9 @@ import javax.inject.Inject
class VpnQuickTile : TileService() {

@Inject
lateinit var dataStoreManager: DataStoreManager
lateinit var gatewayRepository: GatewayRepository

lateinit var settingsRepository: SettingsRepository

private val scope = CoroutineScope(Dispatchers.IO)

Expand Down Expand Up @@ -75,27 +78,31 @@ class VpnQuickTile : TileService() {
VpnState.Up -> NymVpnClient.disconnect(this)
VpnState.Down -> {
scope.launch {
val firstHopCountry = dataStoreManager.getFromStore(DataStoreManager.FIRST_HOP_COUNTRY)
val lastHopCountry = dataStoreManager.getFromStore(DataStoreManager.LAST_HOP_COUNTRY)
val mode = dataStoreManager.getFromStore(DataStoreManager.VPN_MODE)
NymVpnClient.connectForeground(this@VpnQuickTile,
Hop.Country.from(firstHopCountry), Hop.Country.from(lastHopCountry), VpnMode.from(mode))
val entryCountry = gatewayRepository.getFirstHopCountry()
val exitCountry = gatewayRepository.getLastHopCountry()
val mode = settingsRepository.getVpnMode()
val entry = entryCountry.toEntryPoint()
val exit = exitCountry.toExitPoint()
NymVpnClient.configure(entry,exit,mode)
NymVpnClient.start(this@VpnQuickTile)
NymVpn.requestTileServiceStateUpdate(this@VpnQuickTile)
}

}
else -> Unit
}
}
}

private fun setTileText() = scope.launch {
val firstHopCountry = dataStoreManager.getFromStore(DataStoreManager.FIRST_HOP_COUNTRY)
val lastHopCountry = dataStoreManager.getFromStore(DataStoreManager.LAST_HOP_COUNTRY)
val mode = dataStoreManager.getFromStore(DataStoreManager.VPN_MODE)
val isTwoHop = VpnMode.from(mode) == VpnMode.TWO_HOP_MIXNET
val firstHopCountry = gatewayRepository.getFirstHopCountry()
val lastHopCountry = gatewayRepository.getLastHopCountry()
val mode = settingsRepository.getVpnMode()
val isTwoHop = mode == VpnMode.TWO_HOP_MIXNET
setTitle("${this@VpnQuickTile.getString(R.string.mode)}: ${if(isTwoHop) this@VpnQuickTile.getString(
R.string.two_hop) else this@VpnQuickTile.getString(R.string.five_hop)}")
setTileDescription(
"${Hop.Country.from(firstHopCountry).isoCode} -> ${Hop.Country.from(lastHopCountry).isoCode}")
"${firstHopCountry.isoCode} -> ${lastHopCountry.isoCode}")
}

private fun setActive() {
Expand Down
23 changes: 7 additions & 16 deletions app/src/main/java/net/nymtech/nymvpn/ui/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import net.nymtech.nymvpn.util.Constants
import net.nymtech.nymvpn.util.FileUtils
import net.nymtech.nymvpn.util.log.NymLibException
import net.nymtech.vpn.NymVpnClient
import net.nymtech.vpn.model.Hop
import net.nymtech.vpn.model.Country
import timber.log.Timber
import java.time.Instant
import java.util.Locale
Expand Down Expand Up @@ -103,38 +103,29 @@ class AppViewModel @Inject constructor(

fun onEntryLocationSelected(selected : Boolean) = viewModelScope.launch {
settingsRepository.setFirstHopSelection(selected)
gatewayRepository.setFirstHopCountry(Hop.Country(isDefault = true))
gatewayRepository.setFirstHopCountry(Country(isDefault = true))
updateFirstHopDefaultCountry()
}

private suspend fun updateFirstHopDefaultCountry() {
val firstHop = gatewayRepository.getFirstHopCountry()
if(firstHop.isDefault || firstHop.isFastest) {
if(firstHop.isDefault || firstHop.isLowLatency) {
setFirstHopToLowLatency()
}
}

private suspend fun updateEntryCountriesCache() {
val entryGateways = NymVpnClient.gateways(false)
val entryCountries = entryGateways.map {
val countryIso = it
Hop.Country(countryIso, Locale(countryIso.lowercase(), countryIso).displayCountry)
}.toSet()
val entryCountries = NymVpnClient.gateways(false)
gatewayRepository.setEntryCountries(entryCountries)
}

private suspend fun updateExitCountriesCache() {
val exitGateways = NymVpnClient.gateways(true)
val exitCountries = exitGateways.map {
val countryIso = it
Hop.Country(countryIso, Locale(countryIso.lowercase(), countryIso).displayCountry)
}.toSet()
val exitCountries = NymVpnClient.gateways(true)
gatewayRepository.setExitCountries(exitCountries)
}
private suspend fun setFirstHopToLowLatency() {
val lowLatencyCountryIso = NymVpnClient.getLowLatencyEntryCountryCode()
Timber.i("Setting low latency entry country: $lowLatencyCountryIso")
gatewayRepository.setFirstHopCountry(Hop.Country(isoCode = lowLatencyCountryIso, isFastest = true))
val lowLatencyCountry = NymVpnClient.getLowLatencyEntryCountryCode()
gatewayRepository.setFirstHopCountry(lowLatencyCountry)
}

fun openWebPage(url: String) {
Expand Down
Loading

0 comments on commit d509c44

Please sign in to comment.