Skip to content

Commit

Permalink
Merge pull request #47 from Universite-Gustave-Eiffel/feature/setting…
Browse files Browse the repository at this point in the history
…s-screen

# Description

Adds a settings screen to tweak persistent app settings

## Changes

- Added a crossplaform user settings service to manage persistently stored settings using the [Settings Multiplatform](https://github.com/russhwolf/multiplatform-settings) library
  - Android settings are stored using `SharedPreferences`
  - iOS settings are stored using `NSUserDefaults`
  - Web settings are stored using `Storage`
  - Any serializable type can be stored, associated to a unique key and a default value
  - Settings values can be retrieved as a one time thing or as a `Flow<T>` to listen to value changes
- Added a settings screen with a list of all settings
- Bumped dependencies to latest versions
 
## Linked issues

#44 

## Remaining TODOs

UI could still be improved (especially on small phones and on web) but I'm saving this for later when we'll do a general UI/UX improvement work over the whole app to ensure style consistency.

## Checklist

- [x] Code compiles correctly on all platforms
- [x] All pre-existing tests are passing
- [x] If needed, new tests have been added
- [x] Extended the README / documentation if necessary
- [x] Added code has been documented
  • Loading branch information
nicolas-f authored Nov 21, 2024
2 parents c87eb1d + 4baf66d commit 6f8cfde
Show file tree
Hide file tree
Showing 60 changed files with 1,593 additions and 115 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/code_review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ on:
push:
branches:
- main
- dev
pull_request:
types: [ opened, reopened, labeled, unlabeled, ready_for_review, synchronize ]
branches:
- main
- dev

jobs:
SetUp:
Expand Down
10 changes: 8 additions & 2 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig

plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsCompose)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.serialization)
}

kotlin {
Expand Down Expand Up @@ -59,24 +60,29 @@ kotlin {
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.preference)
implementation(libs.koin.android)
}
commonMain.dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.material3)
implementation(compose.materialIconsExtended)
implementation(compose.ui)
implementation(compose.components.resources)
implementation(compose.components.uiToolingPreview)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.viewmodel.compose)
implementation(libs.androidx.runtime.compose)
implementation(libs.koin.core)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.datetime)
implementation(libs.kotlinx.serialization)
implementation(libs.settings.multiplatform)
implementation(libs.settings.multiplatform.serialization)
implementation(libs.settings.multiplatform.coroutines)
}
commonTest.dependencies {
implementation(kotlin("test"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.noiseplanet.noisecapture

import AndroidLogger

import androidx.preference.PreferenceManager
import com.russhwolf.settings.Settings
import com.russhwolf.settings.SharedPreferencesSettings
import org.koin.core.module.Module
import org.koin.core.parameter.parametersOf
import org.koin.dsl.module
Expand All @@ -24,4 +26,9 @@ val platformModule: Module = module {
parametersOf("AudioSource")
})
}

single<Settings> {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(get())
SharedPreferencesSettings(sharedPreferences)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.noiseplanet.noisecapture.util.shadow

import android.graphics.BlurMaskFilter
import androidx.compose.ui.graphics.NativePaint


actual fun NativePaint.setBlurMaskFilter(blurRadius: Float) {
this.maskFilter = BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL)
}
2 changes: 2 additions & 0 deletions composeApp/src/androidUnitTest/kotlin/IgnoreUtil.android.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")

import org.junit.Ignore

/**
Expand Down
91 changes: 88 additions & 3 deletions composeApp/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
<string name="menu_feedback">Measurement feedback</string>
<string name="menu_statistics">Measurements statistics</string>
<string name="menu_map">Last measurement map</string>
<string name="menu_help">Help</string>
<string name="menu_about">About</string>
<string name="menu_calibration">Calibration</string>
<string name="menu_settings">Settings</string>

<!-- PermissionScreen -->
Expand All @@ -26,4 +23,92 @@

<!-- MeasurementScreen -->
<string name="measurement_title">New measurement</string>


<!-- Settings -->
<string name="settings_title">Settings</string>

<string name="settings_section_user_profile">USER PROFILE</string>
<string name="settings_user_acoustics_knowledge_title">Acoustics knowledge</string>
<string name="settings_user_acoustics_knowledge_description">How would you rank your knowledge about acoustics and sonic environments?</string>

<string name="settings_section_general">GENERAL</string>
<string name="settings_general_tooltips_title">Tooltips</string>
<string name="settings_general_tooltips_description">Show tooltips across the NoiseCapture app</string>
<string name="settings_general_disclaimer_title">Disclaimer</string>
<string name="settings_general_disclaimer_description">Show a usage disclaimer upon opening the app</string>
<string name="settings_general_notification_title">Notification</string>
<string name="settings_general_notification_description">Show a notification with a link to the Noise-Planet website (TODO: When is this notification shown?)</string>
<string name="settings_general_automatic_transfer_title">Automatic transfer</string>
<string name="settings_general_automatic_transfer_description">Transfer measurements automatically after completion</string>
<string name="settings_general_wifi_only_title">Wi-Fi only</string>
<string name="settings_general_wifi_only_description">Only transfer measurements data over Wi-Fi to reduce mobile data consumption</string>

<string name="settings_section_measurements">MEASUREMENTS</string>
<string name="settings_measurements_windowing_title">Windowing mode</string>
<string name="settings_measurements_windowing_description">Real time measurement windowing mode (doesn't affect the end result)</string>
<string name="settings_measurements_limit_duration_title">Limit duration</string>
<string name="settings_measurements_limit_duration_description">Limit measurements duration to a certain length</string>
<string name="settings_measurements_max_duration_title">Max duration (in seconds)</string>
<string name="settings_measurements_max_duration_description">Measurements will automatically stop after this amount of time has elapsed</string>
<string name="settings_measurements_spectrogram_mode_title">Spectrogram mode</string>
<string name="settings_measurements_spectrogram_mode_description">Set the spectrogram scale mode (logarithmic or linear)</string>

<string name="settings_section_calibration">CALIBRATION</string>
<string name="settings_calibration_gain_correction_title">Signal gain correction (dB)</string>
<string name="settings_calibration_gain_correction_description">Adds a gain correction to the input signal after calibration</string>
<string name="settings_calibration_countdown_title">Countdown duration (seconds)</string>
<string name="settings_calibration_countdown_description">How many seconds will elapse before the calibration starts</string>
<string name="settings_calibration_duration_title">Calibration duration (seconds)</string>
<string name="settings_calibration_duration_description">Sets the duration of the calibration in seconds</string>
<string name="settings_calibration_output_title">Signal output</string>
<string name="settings_calibration_output_description">Pick the audio output used for the linearity test</string>

<string name="settings_section_map">MAP</string>
<string name="settings_map_measurements_count_title">Shown measurements count</string>
<string name="settings_map_measurements_count_description">Adjust the maximum number of measurements shown on the map at once (set this to 0 to show all measurements)</string>


<!-- User acoustics knowledge -->
<string name="acoustics_knowledge_beginner_title">Beginner</string>
<string name="acoustics_knowledge_beginner_description">I know nothing or only a few things about noise</string>
<string name="acoustics_knowledge_confirmed_title">Confirmed</string>
<string name="acoustics_knowledge_confirmed_description">I have some knowledge about noise</string>
<string name="acoustics_knowledge_expert_title">Expert</string>
<string name="acoustics_knowledge_expert_description">I am an expert or professional in the domain of noise</string>


<!-- Spectrogram scale mode -->
<string name="spectrogram_scale_mode_linear_title">Linear</string>
<string name="spectrogram_scale_mode_linear_description">Linear scale</string>
<string name="spectrogram_scale_mode_logarithmic_title">Log</string>
<string name="spectrogram_scale_mode_logarithmic_description">Logarithmic scale</string>


<!-- Calibration test audio output -->
<string name="calibration_output_phonecall_title">Phone call</string>
<string name="calibration_output_phonecall_description">Phone call</string>
<string name="calibration_output_system_sound_title">System sound</string>
<string name="calibration_output_system_sound_description">System sound</string>
<string name="calibration_output_ringtone_title">Ringtone</string>
<string name="calibration_output_ringtone_description">Ringtone</string>
<string name="calibration_output_music_title">Music</string>
<string name="calibration_output_music_description">Music</string>
<string name="calibration_output_alarm_title">Alarm</string>
<string name="calibration_output_alarm_description">Alarm</string>
<string name="calibration_output_notification_title">Notification</string>
<string name="calibration_output_notification_description">Notification</string>
<string name="calibration_output_dmtf_title">DMTF</string>
<string name="calibration_output_dmtf_description">Dual tone multi-frequency (DMTF)</string>


<!-- Measurement windowing mode -->
<string name="measurement_windowing_mode_hann_title">Hann</string>
<string name="measurement_windowing_mode_hann_description">Hann</string>
<string name="measurement_windowing_mode_rect_title">Rect</string>
<string name="measurement_windowing_mode_rect_description">Rectangular</string>


<!-- Misc -->
<string name="na">N/A</string>
</resources>
2 changes: 1 addition & 1 deletion composeApp/src/commonMain/kotlin/App.kt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import androidx.compose.material.MaterialTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.koin.compose.KoinContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,41 @@ package org.noiseplanet.noisecapture

import org.koin.core.KoinApplication
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.module.Module
import org.koin.dsl.module
import org.noiseplanet.noisecapture.measurements.DefaultMeasurementService
import org.noiseplanet.noisecapture.measurements.MeasurementsService
import org.noiseplanet.noisecapture.permission.defaultPermissionModule
import org.noiseplanet.noisecapture.permission.platformPermissionModule
import org.noiseplanet.noisecapture.services.servicesModule
import org.noiseplanet.noisecapture.ui.features.home.homeModule
import org.noiseplanet.noisecapture.ui.features.measurement.measurementModule
import org.noiseplanet.noisecapture.ui.features.permission.requestPermissionModule
import org.noiseplanet.noisecapture.ui.features.settings.settingsModule

/**
* Create root Koin application and register modules shared between platforms
*/
fun initKoin(
additionalModules: List<Module> = emptyList(),
): KoinApplication {

stopKoin()

return startKoin {
modules(
module {
includes(additionalModules)
},

module {
single<MeasurementsService> {
DefaultMeasurementService(audioSource = get(), logger = get())
}
},
servicesModule,

defaultPermissionModule,
platformPermissionModule(),

homeModule,
requestPermissionModule,
measurementModule,
settingsModule,
)
createEagerInstances()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.Scaffold
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
Expand All @@ -18,6 +18,7 @@ import org.noiseplanet.noisecapture.ui.AppBar
import org.noiseplanet.noisecapture.ui.features.home.HomeScreen
import org.noiseplanet.noisecapture.ui.features.measurement.MeasurementScreen
import org.noiseplanet.noisecapture.ui.features.permission.RequestPermissionScreen
import org.noiseplanet.noisecapture.ui.features.settings.SettingsScreen
import org.noiseplanet.noisecapture.ui.navigation.Route
import org.noiseplanet.noisecapture.ui.navigation.Transitions

Expand Down Expand Up @@ -59,19 +60,24 @@ fun NoiseCaptureApp() {
.windowInsetsPadding(WindowInsets.navigationBars)
) {
composable(route = Route.Home.name) {
// TODO: Silently check for permissions and bypass this step if they are already all granted
HomeScreen(navigationController = navController)
}

composable(route = Route.RequestPermission.name) {
// TODO: Silently check for permissions and bypass this step if
// they are already all granted
RequestPermissionScreen(
onClickNextButton = {
navController.navigate(Route.Measurement.name)
}
)
}

composable(route = Route.Measurement.name) {
MeasurementScreen()
}

composable(route = Route.Settings.name) { SettingsScreen() }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.noiseplanet.noisecapture.model

import kotlinx.serialization.Serializable
import noisecapture.composeapp.generated.resources.Res
import noisecapture.composeapp.generated.resources.acoustics_knowledge_beginner_description
import noisecapture.composeapp.generated.resources.acoustics_knowledge_beginner_title
import noisecapture.composeapp.generated.resources.acoustics_knowledge_confirmed_description
import noisecapture.composeapp.generated.resources.acoustics_knowledge_confirmed_title
import noisecapture.composeapp.generated.resources.acoustics_knowledge_expert_description
import noisecapture.composeapp.generated.resources.acoustics_knowledge_expert_title
import org.jetbrains.compose.resources.StringResource
import org.noiseplanet.noisecapture.util.IterableEnum
import org.noiseplanet.noisecapture.util.ShortNameRepresentable
import kotlin.enums.EnumEntries

@Serializable
enum class AcousticsKnowledgeLevel : IterableEnum<AcousticsKnowledgeLevel>, ShortNameRepresentable {

BEGINNER {

override val fullName: StringResource = Res.string.acoustics_knowledge_beginner_description
override val shortName: StringResource = Res.string.acoustics_knowledge_beginner_title
},

CONFIRMED {

override val fullName: StringResource = Res.string.acoustics_knowledge_confirmed_description
override val shortName: StringResource = Res.string.acoustics_knowledge_confirmed_title
},

EXPERT {

override val fullName: StringResource = Res.string.acoustics_knowledge_expert_description
override val shortName: StringResource = Res.string.acoustics_knowledge_expert_title
};

override fun entries(): EnumEntries<AcousticsKnowledgeLevel> = entries
}
Loading

0 comments on commit 6f8cfde

Please sign in to comment.