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

Commit

Permalink
feat: ui enhancements
Browse files Browse the repository at this point in the history
Improved UI look and feel with various enhancements.

Fixed bug where VPN was stopping after a while due to lib errors that can be ignored.

Added splash screen activity better initial application data and state while splash is showing.

Added analytics screen for alpha to present users with options to send anonymous analytics.

Added skeleton of android/lib ci workflow.
  • Loading branch information
zaneschepke committed Apr 18, 2024
1 parent f27ed04 commit f40bbdf
Show file tree
Hide file tree
Showing 49 changed files with 1,006 additions and 780 deletions.
1 change: 1 addition & 0 deletions .github/workflows/cd-pre-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
<br />
SHA256 fingerprint:
```${{ steps.checksum.outputs.checksum }}```
tag_name: ${{ github.ref_name }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/cd-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
body: |
<br />
SHA256 fingerprint:
```${{ steps.checksum.outputs.checksum }}```
tag_name: ${{ github.ref_name }}
Expand Down
6 changes: 3 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ android {
"\"${(System.getenv(Constants.SENTRY_DSN) ?: getLocalProperty("sentry.dsn")) ?: ""}\"",
)
buildConfigField("Boolean", "IS_SANDBOX", "false")
buildConfigField("Boolean", Constants.OPT_IN_REPORTING, "false")
proguardFile("fdroid-rules.pro")
}

Expand All @@ -47,10 +46,11 @@ android {
try {
load(file("signing.properties").reader())
} catch (_: Exception) {
load(file("signing_template.properties").reader())
// can't find signing file, use debug
this@create.initWith(signingConfigs.getByName("debug"))
return@create
}
}

// try to get secrets from env first for pipeline build, then properties file for local
// build
storeFile =
Expand Down
14 changes: 11 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@
android:theme="@style/Theme.AppSplashScreen"
tools:node="merge"
tools:targetApi="33">
<activity
android:name=".ui.SplashActivity"
android:exported="true"
android:theme="@style/Theme.AppSplashScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.MainActivity"
android:configChanges="orientation|keyboardHidden"
android:exported="true"
android:theme="@style/Theme.NymVPN"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
</activity>
Expand Down Expand Up @@ -95,4 +103,4 @@
android:value="@string/fullVersionName" />
</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,16 @@ interface SettingsRepository {

suspend fun setErrorReporting(enabled: Boolean)

suspend fun setAnalytics(enabled: Boolean)

suspend fun isAnalyticsEnabled(): Boolean
suspend fun isFirstHopSelectionEnabled(): Boolean

suspend fun setFirstHopSelection(enabled: Boolean)

suspend fun isAnalyticsShown(): Boolean

suspend fun setAnalyticsShown(shown: Boolean)

val settingsFlow: Flow<Settings>
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ class DataStoreManager(private val context: Context) {
val VPN_MODE = stringPreferencesKey("VPN_MODE")
val FIRST_HOP_SELECTION = booleanPreferencesKey("FIRST_HOP_SELECTION")
val ERROR_REPORTING = booleanPreferencesKey("ERROR_REPORTING")
val ANALYTICS = booleanPreferencesKey("ANALYTICS")
val AUTO_START = booleanPreferencesKey("AUTO_START")
val LOGGED_IN = booleanPreferencesKey("LOGGED_IN")
val ANALYTICS_SHOWN = booleanPreferencesKey("ANALYTICS_SHOWN")

// GatewayCountries
val FIRST_HOP_COUNTRY = stringPreferencesKey("FIRST_HOP_COUNTRY")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package net.nymtech.nymvpn.data.datastore

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import net.nymtech.nymvpn.BuildConfig
import net.nymtech.nymvpn.data.SettingsRepository
import net.nymtech.nymvpn.data.model.Settings
import net.nymtech.nymvpn.ui.theme.Theme
Expand Down Expand Up @@ -61,13 +60,21 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager

override suspend fun isErrorReportingEnabled(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.ERROR_REPORTING)
?: BuildConfig.OPT_IN_REPORTING
?: Settings.REPORTING_DEFAULT
}

override suspend fun setErrorReporting(enabled: Boolean) {
dataStoreManager.saveToDataStore(DataStoreManager.ERROR_REPORTING, enabled)
}

override suspend fun setAnalytics(enabled: Boolean) {
dataStoreManager.saveToDataStore(DataStoreManager.ANALYTICS, enabled)
}

override suspend fun isAnalyticsEnabled(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.ANALYTICS) ?: Settings.REPORTING_DEFAULT
}

override suspend fun isFirstHopSelectionEnabled(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.FIRST_HOP_SELECTION)
?: Settings.FIRST_HOP_SELECTION_DEFAULT
Expand All @@ -77,6 +84,14 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager
dataStoreManager.saveToDataStore(DataStoreManager.FIRST_HOP_SELECTION, enabled)
}

override suspend fun isAnalyticsShown(): Boolean {
return dataStoreManager.getFromStore(DataStoreManager.ANALYTICS_SHOWN) ?: Settings.ANALYTICS_SHOWN_DEFAULT
}

override suspend fun setAnalyticsShown(shown: Boolean) {
dataStoreManager.saveToDataStore(DataStoreManager.ANALYTICS_SHOWN, shown)
}

override val settingsFlow: Flow<Settings> =
dataStoreManager.preferencesFlow.map { prefs ->
prefs?.let { pref ->
Expand All @@ -93,11 +108,14 @@ class DataStoreSettingsRepository(private val dataStoreManager: DataStoreManager
?: Settings.AUTO_START_DEFAULT,
errorReportingEnabled =
pref[DataStoreManager.ERROR_REPORTING]
?: BuildConfig.OPT_IN_REPORTING,
?: Settings.REPORTING_DEFAULT,
analyticsEnabled = pref[DataStoreManager.ANALYTICS]
?: Settings.REPORTING_DEFAULT,
firstHopSelectionEnabled =
pref[DataStoreManager.FIRST_HOP_SELECTION]
?: Settings.FIRST_HOP_SELECTION_DEFAULT,
loggedIn = pref[DataStoreManager.LOGGED_IN] ?: Settings.LOGGED_IN_DEFAULT,
isAnalyticsShown = pref[DataStoreManager.ANALYTICS_SHOWN] ?: Settings.ANALYTICS_SHOWN_DEFAULT,
)
} catch (e: IllegalArgumentException) {
Timber.e(e)
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/net/nymtech/nymvpn/data/model/Settings.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package net.nymtech.nymvpn.data.model

import net.nymtech.nymvpn.BuildConfig
import net.nymtech.nymvpn.ui.theme.Theme
import net.nymtech.vpn.model.VpnMode

data class Settings(
val theme: Theme = Theme.default(),
val vpnMode: VpnMode = VpnMode.default(),
val autoStartEnabled: Boolean = AUTO_START_DEFAULT,
val errorReportingEnabled: Boolean = BuildConfig.OPT_IN_REPORTING,
val errorReportingEnabled: Boolean = REPORTING_DEFAULT,
val analyticsEnabled: Boolean = REPORTING_DEFAULT,
val firstHopSelectionEnabled: Boolean = FIRST_HOP_SELECTION_DEFAULT,
val loggedIn: Boolean = LOGGED_IN_DEFAULT,
val isAnalyticsShown: Boolean = ANALYTICS_SHOWN_DEFAULT,
) {
companion object {
const val FIRST_HOP_SELECTION_DEFAULT = false
const val AUTO_START_DEFAULT = false
const val LOGGED_IN_DEFAULT = false
const val REPORTING_DEFAULT = false
const val ANALYTICS_SHOWN_DEFAULT = false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
import net.nymtech.vpn.NymApi
import net.nymtech.nymvpn.NymVpn
import net.nymtech.vpn.NymApi

@Module
@InstallIn(ViewModelComponent::class)
Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/net/nymtech/nymvpn/ui/AppUiState.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package net.nymtech.nymvpn.ui

import net.nymtech.nymvpn.ui.theme.Theme
import net.nymtech.nymvpn.data.model.Settings
import net.nymtech.vpn.model.VpnState

data class AppUiState(
val loading: Boolean = true,
val theme: Theme = Theme.AUTOMATIC,
val loggedIn: Boolean = false,
val snackbarMessage: String = "",
val snackbarMessageConsumed: Boolean = true,
val vpnState: VpnState = VpnState.Down,
val settings: Settings = Settings(),
)
20 changes: 18 additions & 2 deletions app/src/main/java/net/nymtech/nymvpn/ui/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.nymtech.logcathelper.LogcatHelper
import net.nymtech.logcathelper.model.LogLevel
import net.nymtech.logcathelper.model.LogMessage
Expand Down Expand Up @@ -53,11 +54,10 @@ constructor(
combine(_uiState, settingsRepository.settingsFlow, vpnClient.stateFlow) { state, settings, vpnState ->
AppUiState(
false,
settings.theme,
settings.loggedIn,
state.snackbarMessage,
state.snackbarMessageConsumed,
vpnState.vpnState,
settings,
)
}.stateIn(
viewModelScope,
Expand Down Expand Up @@ -118,6 +118,14 @@ constructor(
}
}

suspend fun isAnalyticsShown() = withContext(viewModelScope.coroutineContext + Dispatchers.Main) {
settingsRepository.isAnalyticsEnabled()
}

fun setAnalyticsShown() = viewModelScope.launch {
settingsRepository.setAnalyticsShown(true)
}

fun onEntryLocationSelected(selected: Boolean) = viewModelScope.launch {
settingsRepository.setFirstHopSelection(selected)
gatewayRepository.setFirstHopCountry(Country(isDefault = true))
Expand All @@ -131,6 +139,14 @@ constructor(
}
}

fun onErrorReportingSelected() = viewModelScope.launch {
settingsRepository.setErrorReporting(!uiState.value.settings.errorReportingEnabled)
}

fun onAnalyticsReportingSelected() = viewModelScope.launch {
settingsRepository.setAnalytics(!uiState.value.settings.analyticsEnabled)
}

private suspend fun updateEntryCountriesCache() {
try {
val entryCountries = nymApi.gateways(false)
Expand Down
Loading

0 comments on commit f40bbdf

Please sign in to comment.