From 8d673031aa9d95610b24b11b2501e5b608193144 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:41:59 +0100 Subject: [PATCH 001/111] declare repository interface --- .../features/settings/domain/data/AppPreferencesRepository.kt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt new file mode 100644 index 00000000..74185a77 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data + +interface AppPreferencesRepository { +} From 162a1c39716068181980683dc5d910535930a4bd Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:42:41 +0100 Subject: [PATCH 002/111] declare data source interfaces --- .../domain/data/local/LocalAccessPortPreferencesDataSource.kt | 3 +++ .../domain/data/local/LocalGraphSizePreferencesDataSource.kt | 3 +++ .../domain/data/local/LocalLanguagePreferencesDataSource.kt | 3 +++ .../data/local/LocalProximityLockPreferencesDataSource.kt | 3 +++ .../domain/data/local/LocalUiConfigPreferencesDataSource.kt | 3 +++ 5 files changed, 15 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt new file mode 100644 index 00000000..aa5bb998 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalAccessPortPreferencesDataSource {} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt new file mode 100644 index 00000000..42cce576 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalGraphSizePreferencesDataSource diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt new file mode 100644 index 00000000..bef3af90 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalLanguagePreferencesDataSource diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt new file mode 100644 index 00000000..684e0768 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalProximityLockPreferencesDataSource diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt new file mode 100644 index 00000000..9fbc3ace --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalUiConfigPreferencesDataSource From 9673029228e6ab1ce228ea1858b01fbbbf4b4d93 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:42:52 +0100 Subject: [PATCH 003/111] declare wrapper data source --- .../domain/data/local/LocalAppPreferencesDataSource.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt new file mode 100644 index 00000000..14e67b1a --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local + +interface LocalAppPreferencesDataSource : + LocalAccessPortPreferencesDataSource, + LocalGraphSizePreferencesDataSource, + LocalLanguagePreferencesDataSource, + LocalProximityLockPreferencesDataSource, + LocalUiConfigPreferencesDataSource + From 470f1b4bcb2a9987abf21a6ea05695c22cbfff9b Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:43:08 +0100 Subject: [PATCH 004/111] declare default repository implementation --- .../settings/data/DefaultAppPreferencesRepository.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt new file mode 100644 index 00000000..0d15e594 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt @@ -0,0 +1,12 @@ +package de.rwth_aachen.phyphox.features.settings.data + +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import javax.inject.Inject + + +class DefaultAppPreferencesRepository @Inject constructor( + private val localAppPreferencesDataSource: LocalAppPreferencesDataSource +) : AppPreferencesRepository{ + +} From f710d8a9e4c20be69138f251786dddf126309a53 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:43:24 +0100 Subject: [PATCH 005/111] declare default data source implementation --- .../data/local/DefaultLocalAppPreferencesDataSource.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt new file mode 100644 index 00000000..9b9651a0 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt @@ -0,0 +1,6 @@ +package de.rwth_aachen.phyphox.features.settings.data.local + +import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource + +class DefaultLocalAppPreferencesDataSource: LocalAppPreferencesDataSource { +} From 5d04d30c4d6e95ba60cbf98ea32860de89aa1108 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 13:43:39 +0100 Subject: [PATCH 006/111] declare usecases --- .../domain/usecase/accessport/GetAccessPortRangeUseCase.kt | 4 ++++ .../domain/usecase/accessport/GetCurrentAccessPortUseCase.kt | 4 ++++ .../domain/usecase/accessport/SetCurrentAccessPortUseCase.kt | 4 ++++ .../domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt | 4 ++++ .../domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt | 4 ++++ .../settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt | 4 ++++ .../domain/usecase/language/GetCurrentAppLanguageUseCase.kt | 4 ++++ .../domain/usecase/language/GetSupportedLanguagesUseCase.kt | 4 ++++ .../settings/domain/usecase/language/SetAppLanguageUseCase.kt | 4 ++++ .../proximitylock/IsCurrentProximityLockEnabledUseCase.kt | 4 ++++ .../usecase/proximitylock/SetProximityLockStatusUseCase.kt | 4 ++++ .../settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt | 4 ++++ .../settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt | 4 ++++ 13 files changed, 52 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt new file mode 100644 index 00000000..430ba35c --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport + +class GetAccessPortRangeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt new file mode 100644 index 00000000..a4d8bbf8 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport + +class GetCurrentAccessPortUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt new file mode 100644 index 00000000..ae7b3e82 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport + +class SetCurrentAccessPortUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt new file mode 100644 index 00000000..535009f7 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +class GetCurrentGraphSizeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt new file mode 100644 index 00000000..580f404e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +class GetGraphSizeRangeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt new file mode 100644 index 00000000..35a59d75 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +class SetGraphSizeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt new file mode 100644 index 00000000..6c10bd8a --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.language + +class GetCurrentAppLanguageUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt new file mode 100644 index 00000000..4f332d72 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.language + +class GetSupportedLanguagesUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt new file mode 100644 index 00000000..a8d7484d --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.language + +class SetAppLanguageUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt new file mode 100644 index 00000000..ba17c7fb --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock + +class IsCurrentProximityLockEnabledUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt new file mode 100644 index 00000000..c1950dc6 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock + +class SetProximityLockStatusUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt new file mode 100644 index 00000000..d3bea886 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode + +class GetCurrentUiModeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt new file mode 100644 index 00000000..94f0465e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode + +class SetCurrentUiModeUseCase { +} From 25b53dcc69a5feb8719d8200b86439db93a8b61b Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 14:00:20 +0100 Subject: [PATCH 007/111] move settings activity and fragment to new package --- app/src/main/AndroidManifest.xml | 2 +- .../phyphox/ExperimentList/ExperimentListActivity.java | 8 ++------ .../java/de/rwth_aachen/phyphox/Helper/Helper.java | 10 +--------- .../presentation/composeable}/SettingsActivity.java | 6 +----- .../presentation/composeable}/SettingsFragment.java | 2 +- 5 files changed, 6 insertions(+), 22 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/{SettingsActivity => features/settings/presentation/composeable}/SettingsActivity.java (87%) rename app/src/main/java/de/rwth_aachen/phyphox/{SettingsActivity => features/settings/presentation/composeable}/SettingsFragment.java (98%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72d496e5..65d8bd33 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -94,7 +94,7 @@ android:value="de.rwth_aachen.phyphox.ExperimentList.ExperimentListActivity" /> Date: Wed, 31 Dec 2025 14:10:07 +0100 Subject: [PATCH 008/111] Rename .java to .kt --- .../composeable/{SettingsActivity.java => SettingsActivity.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/{SettingsActivity.java => SettingsActivity.kt} (100%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.java b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt similarity index 100% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.java rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt From 4e4df337293cfa3c2a6c61f4428ed1e13a8180dd Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 14:10:07 +0100 Subject: [PATCH 009/111] migrate SettingsActivity to koltin --- .../composeable/SettingsActivity.kt | 86 ++++++++++++------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt index 528cb496..51171617 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt @@ -1,39 +1,67 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable; +package de.rwth_aachen.phyphox.features.settings.presentation.composeable +import android.os.Bundle +import android.view.View +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import dagger.hilt.android.AndroidEntryPoint +import de.rwth_aachen.phyphox.Helper.WindowInsetHelper +import de.rwth_aachen.phyphox.R -import androidx.activity.EdgeToEdge; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import android.os.Bundle; +@AndroidEntryPoint +class SettingsActivity : AppCompatActivity() { + private lateinit var toolbar: Toolbar + private lateinit var settingsFrame:View + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.enableEdgeToEdge() + setContentView(R.layout.activity_settings) + inflateViews() + setSupportActionBar(toolbar) + updateActionBar() + setFrameInsets() + setToolBarInsets() + showSettings() + } -import dagger.hilt.android.AndroidEntryPoint; -import de.rwth_aachen.phyphox.Helper.WindowInsetHelper; -import de.rwth_aachen.phyphox.R; + private fun inflateViews() { + toolbar = findViewById(R.id.settingsToolbar) + settingsFrame = findViewById(R.id.settingsFrame) + } -@AndroidEntryPoint -public class SettingsActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - EdgeToEdge.enable(this); - setContentView(R.layout.activity_settings); - - SettingsFragment settingsFragment = new SettingsFragment(); - getSupportFragmentManager().beginTransaction().replace(R.id.settingsFrame, settingsFragment).commit(); - - Toolbar toolbar = (Toolbar) findViewById(R.id.settingsToolbar); - setSupportActionBar(toolbar); - - ActionBar ab = getSupportActionBar(); - if (ab != null) { - ab.setDisplayHomeAsUpEnabled(true); - ab.setDisplayShowTitleEnabled(true); + private fun updateActionBar() { + supportActionBar?.apply { + setDisplayHomeAsUpEnabled(true) + setDisplayShowTitleEnabled(true) } - WindowInsetHelper.setInsets(findViewById(R.id.settingsFrame), WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.IGNORE, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.MARGIN); - WindowInsetHelper.setInsets(findViewById(R.id.settingsToolbar), WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.IGNORE); + } + + private fun showSettings() { + supportFragmentManager + .beginTransaction() + .replace(R.id.settingsFrame, SettingsFragment()) + .commit() + } + private fun setFrameInsets(){ + WindowInsetHelper.setInsets( + settingsFrame, + WindowInsetHelper.ApplyTo.PADDING, + WindowInsetHelper.ApplyTo.IGNORE, + WindowInsetHelper.ApplyTo.PADDING, + WindowInsetHelper.ApplyTo.MARGIN, + ) + } + private fun setToolBarInsets() { + WindowInsetHelper.setInsets( + toolbar, + WindowInsetHelper.ApplyTo.PADDING, + WindowInsetHelper.ApplyTo.PADDING, + WindowInsetHelper.ApplyTo.PADDING, + WindowInsetHelper.ApplyTo.IGNORE, + ) } } From 467e256cae8ec0096d10d014e32487def5b594b6 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 14:10:25 +0100 Subject: [PATCH 010/111] remove unused lines --- .../settings/presentation/composeable/SettingsActivity.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt index 51171617..5200bb52 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt @@ -9,11 +9,10 @@ import dagger.hilt.android.AndroidEntryPoint import de.rwth_aachen.phyphox.Helper.WindowInsetHelper import de.rwth_aachen.phyphox.R - @AndroidEntryPoint class SettingsActivity : AppCompatActivity() { private lateinit var toolbar: Toolbar - private lateinit var settingsFrame:View + private lateinit var settingsFrame: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -45,7 +44,8 @@ class SettingsActivity : AppCompatActivity() { .replace(R.id.settingsFrame, SettingsFragment()) .commit() } - private fun setFrameInsets(){ + + private fun setFrameInsets() { WindowInsetHelper.setInsets( settingsFrame, WindowInsetHelper.ApplyTo.PADDING, From 584c3e8c6c86301c6d01e95ead64ea33d19c4de6 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 15:34:17 +0100 Subject: [PATCH 011/111] declare a viewmodel --- .../presentation/viewmodel/SettingsViewModel.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt new file mode 100644 index 00000000..19725e8e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -0,0 +1,14 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +@HiltViewModel +class SettingsViewModel : ViewModel() { + + private val _uiState = MutableStateFlow(SettingsUiState()) + val uiState = _uiState.asStateFlow() + +} From 48351d8da33e5f101ea1e42342ce998f7f90c99a Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 15:34:38 +0100 Subject: [PATCH 012/111] declare string uimodel --- .../phyphox/ui/string/StringUIModel.kt | 19 ++++++++++++++ .../phyphox/ui/string/StringUIModelExt.kt | 25 +++++++++++++++++++ .../phyphox/ui/string/TextStringUIModel.kt | 8 ++++++ 3 files changed, 52 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt new file mode 100644 index 00000000..103901cf --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt @@ -0,0 +1,19 @@ +package de.rwth_aachen.phyphox.ui.string + +import android.content.Context + + +abstract class StringUIModel { + abstract fun resolve(context: Context): String + + protected fun resolveVarArgs(context: Context, args: Array): Array { + return args.map { + if (it is StringUIModel) { + it.resolve(context) + } else { + it + } + }.toTypedArray() + } +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt new file mode 100644 index 00000000..f2b71e34 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt @@ -0,0 +1,25 @@ +package de.rwth_aachen.phyphox.ui.string + +import androidx.annotation.StringRes +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import shresthajibesh.cookiejar.lib.text.TextStringUIModel + +val emptyStringUiModel = "".toStringUIModel() + +fun String.toStringUIModel(): StringUIModel = TextStringUIModel(this) +fun String?.toStringOrEmptyUIModel(): StringUIModel = this?.toStringUIModel() ?: emptyStringUiModel + +fun @receiver:StringRes Int.toStringUIModel( + vararg args: Any +): StringUIModel = ResourceStringUIModel( + resId = this, + formatArgs = args +) + +@Composable +fun StringUIModel.resolve() = resolve(LocalContext.current) + + +@Composable +fun StringUIModel?.resolveOrDefault(default: String = ""): String = this?.resolve() ?: default diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt new file mode 100644 index 00000000..b6abdf86 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt @@ -0,0 +1,8 @@ +package shresthajibesh.cookiejar.lib.text + +import android.content.Context +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +class TextStringUIModel(val value: String) : StringUIModel() { + override fun resolve(context: Context): String = value +} From e4ea853c446ddf94113c9769e8a67265e81f4fa2 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 15:38:04 +0100 Subject: [PATCH 013/111] move settings activity again --- app/build.gradle.kts | 5 +++++ app/src/main/AndroidManifest.xml | 2 +- .../phyphox/ExperimentList/ExperimentListActivity.java | 4 ++-- app/src/main/java/de/rwth_aachen/phyphox/Helper/Helper.java | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 72503b8d..f4595e34 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.kotlin.dsl.support.kotlinCompilerOptions + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -129,6 +131,9 @@ android { "UnrememberedMutableState", ) } + kotlinOptions { + freeCompilerArgs = listOf("-XXLanguage:+PropertyParamAnnotationDefaultTargetMode") + } ktlint { android.set(true) ignoreFailures.set(true) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 65d8bd33..c18a4be8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -94,7 +94,7 @@ android:value="de.rwth_aachen.phyphox.ExperimentList.ExperimentListActivity" /> Date: Wed, 31 Dec 2025 15:38:13 +0100 Subject: [PATCH 014/111] define string resource UImodel --- .../ui/string/ResourceStringUIModel.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt new file mode 100644 index 00000000..9573fd6e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt @@ -0,0 +1,35 @@ +package de.rwth_aachen.phyphox.ui.string + +import android.content.Context +import androidx.annotation.StringRes + +class ResourceStringUIModel( + @StringRes val resId: Int, + vararg val formatArgs: Any +) : StringUIModel() { + override fun resolve(context: Context): String { + return if (formatArgs.isEmpty()) { + context.getString(resId, *resolveVarArgs(context, formatArgs)) + } else { + context.getString(resId, *resolveVarArgs(context, formatArgs)) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as ResourceStringUIModel + if (resId != other.resId) return false + if (!formatArgs.contentEquals(other.formatArgs)) return false + return true + } + + override fun hashCode(): Int { + return (9 * resId) + formatArgs.contentHashCode() + } + + override fun toString(): String { + return "ResourceStringUIModel(resId=$resId, formatArgs=${formatArgs.contentToString()})" + } + +} From 1f9ac9077e5a3beecd22825ead68ec988d4c114d Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 15:38:30 +0100 Subject: [PATCH 015/111] move things again --- .../settings/presentation/SettingsActivity.kt | 78 +++++++++++++++++++ .../{composeable => }/SettingsFragment.java | 2 +- 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable => }/SettingsFragment.java (98%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt new file mode 100644 index 00000000..d78a78a1 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -0,0 +1,78 @@ +package de.rwth_aachen.phyphox.features.settings.presentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.compose.runtime.getValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import dagger.hilt.android.AndroidEntryPoint +import de.rwth_aachen.phyphox.features.settings.presentation.composeable.SettingsRoot +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewModel +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@AndroidEntryPoint +class SettingsActivity : ComponentActivity() { + private val viewModel: SettingsViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + this.enableEdgeToEdge() + setContent { + PhyphoxTheme { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + SettingsRoot( + uiState = uiState + ) + } + } +// setContentView(R.layout.activity_settings) +// inflateViews() +// setSupportActionBar(toolbar) +// updateActionBar() +// setFrameInsets() +// setToolBarInsets() +// showSettings() + } + +// private fun inflateViews() { +// toolbar = findViewById(R.id.settingsToolbar) +// settingsFrame = findViewById(R.id.settingsFrame) +// } +// +// private fun updateActionBar() { +// supportActionBar?.apply { +// setDisplayHomeAsUpEnabled(true) +// setDisplayShowTitleEnabled(true) +// } +// } +// +// private fun showSettings() { +// supportFragmentManager +// .beginTransaction() +// .replace(R.id.settingsFrame, SettingsFragment()) +// .commit() +// } +// +// private fun setFrameInsets() { +// WindowInsetHelper.setInsets( +// settingsFrame, +// WindowInsetHelper.ApplyTo.PADDING, +// WindowInsetHelper.ApplyTo.IGNORE, +// WindowInsetHelper.ApplyTo.PADDING, +// WindowInsetHelper.ApplyTo.MARGIN, +// ) +// } +// +// private fun setToolBarInsets() { +// WindowInsetHelper.setInsets( +// toolbar, +// WindowInsetHelper.ApplyTo.PADDING, +// WindowInsetHelper.ApplyTo.PADDING, +// WindowInsetHelper.ApplyTo.PADDING, +// WindowInsetHelper.ApplyTo.IGNORE, +// ) +// } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsFragment.java b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java similarity index 98% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsFragment.java rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java index a9b91735..eba49400 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsFragment.java +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable; +package de.rwth_aachen.phyphox.features.settings.presentation; import android.os.Build; From 08d6553aa21f119e710c68aec575675366aad949 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 15:59:05 +0100 Subject: [PATCH 016/111] declare composables --- .../composeable/PreferenceCategory.kt | 21 ++++ .../composeable/PreferenceItem.kt | 54 ++++++++++ .../composeable/SeekBarPreferenceItem.kt | 33 +++++++ .../composeable/SettingsActivity.kt | 67 ------------- .../composeable/SettingsContent.kt | 99 +++++++++++++++++++ .../presentation/composeable/SettingsRoot.kt | 68 +++++++++++++ .../composeable/SwitchPreferenceItem.kt | 58 +++++++++++ .../presentation/viewmodel/SettingsUiState.kt | 15 +++ .../viewmodel/SettingsViewModel.kt | 3 +- 9 files changed, 350 insertions(+), 68 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt new file mode 100644 index 00000000..38fa1d20 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt @@ -0,0 +1,21 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun PreferenceCategory(title: String) { + Text( + text = title, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp), + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt new file mode 100644 index 00000000..18dbc91a --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt @@ -0,0 +1,54 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +@Composable +fun PreferenceItem( + title: String, + summary: String? = null, + iconRes: Int? = null, + onClick: () -> Unit = {}, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onClick) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (iconRes != null) { + Icon( + painter = painterResource(id = iconRes), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.width(16.dp)) + } + Column(modifier = Modifier.weight(1f)) { + Text(text = title, style = MaterialTheme.typography.bodyLarge) + if (summary != null) { + Text( + text = summary, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt new file mode 100644 index 00000000..de16cce9 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt @@ -0,0 +1,33 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun SeekBarPreferenceItem( + title: String, + value: Float, + valueRange: ClosedFloatingPointRange, + onValueChange: (Float) -> Unit, +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text(text = title, style = MaterialTheme.typography.bodyLarge) + Slider( + value = value, + onValueChange = onValueChange, + valueRange = valueRange, + steps = (valueRange.endInclusive - valueRange.start).toInt() - 1, + ) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt deleted file mode 100644 index 5200bb52..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsActivity.kt +++ /dev/null @@ -1,67 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable - -import android.os.Bundle -import android.view.View -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import dagger.hilt.android.AndroidEntryPoint -import de.rwth_aachen.phyphox.Helper.WindowInsetHelper -import de.rwth_aachen.phyphox.R - -@AndroidEntryPoint -class SettingsActivity : AppCompatActivity() { - private lateinit var toolbar: Toolbar - private lateinit var settingsFrame: View - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - this.enableEdgeToEdge() - setContentView(R.layout.activity_settings) - inflateViews() - setSupportActionBar(toolbar) - updateActionBar() - setFrameInsets() - setToolBarInsets() - showSettings() - } - - private fun inflateViews() { - toolbar = findViewById(R.id.settingsToolbar) - settingsFrame = findViewById(R.id.settingsFrame) - } - - private fun updateActionBar() { - supportActionBar?.apply { - setDisplayHomeAsUpEnabled(true) - setDisplayShowTitleEnabled(true) - } - } - - private fun showSettings() { - supportFragmentManager - .beginTransaction() - .replace(R.id.settingsFrame, SettingsFragment()) - .commit() - } - - private fun setFrameInsets() { - WindowInsetHelper.setInsets( - settingsFrame, - WindowInsetHelper.ApplyTo.PADDING, - WindowInsetHelper.ApplyTo.IGNORE, - WindowInsetHelper.ApplyTo.PADDING, - WindowInsetHelper.ApplyTo.MARGIN, - ) - } - - private fun setToolBarInsets() { - WindowInsetHelper.setInsets( - toolbar, - WindowInsetHelper.ApplyTo.PADDING, - WindowInsetHelper.ApplyTo.PADDING, - WindowInsetHelper.ApplyTo.PADDING, - WindowInsetHelper.ApplyTo.IGNORE, - ) - } -} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt new file mode 100644 index 00000000..05df4705 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt @@ -0,0 +1,99 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsContent( + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier + .fillMaxSize(), + ) { + // Language Category + item { + PreferenceCategory(title = stringResource(id = R.string.settingsHeadLanguage)) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsLanguage), + summary = "English", // Mock summary + iconRes = R.drawable.setting_language, + ) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsTranslation), + summary = stringResource(id = R.string.settingsTranslationMore), + iconRes = R.drawable.setting_translate, + ) + } + + // Graph View Category + item { + PreferenceCategory(title = stringResource(id = R.string.settingGraphViewEdit)) + } + item { + var graphSize by remember { mutableFloatStateOf(1f) } + SeekBarPreferenceItem( + title = "Graph size", // Mock title + value = graphSize, + valueRange = 0f..3f, + onValueChange = { graphSize = it }, + ) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settings_theme_title), + summary = stringResource(id = R.string.settings_mode_dark_system), // Mock summary + iconRes = R.drawable.ic_dark_mode, + ) + } + + // Advanced Category + item { + PreferenceCategory(title = stringResource(id = R.string.settingsHeadAdvanced)) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsPort), + summary = "8080", + iconRes = R.drawable.setting_http, + ) + } + item { + var proximityLock by remember { mutableStateOf(false) } + SwitchPreferenceItem( + title = stringResource(id = R.string.settingsProximityLock), + summary = stringResource(id = R.string.settingsProximityLockDetail), + iconRes = R.drawable.setting_lock, + checked = proximityLock, + onCheckedChange = { proximityLock = it }, + ) + } + } + +} + + +@Preview(showBackground = true) +@Composable +fun SettingsContentPreview() { + PhyphoxTheme { + SettingsContent() + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt new file mode 100644 index 00000000..7f31af22 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt @@ -0,0 +1,68 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.string.toStringUIModel +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsRoot( + modifier: Modifier = Modifier, + uiState: SettingsUiState, +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text(stringResource(id = R.string.action_settings)) } + ) + }, + modifier = modifier + ) { innerPadding -> + SettingsContent( + modifier = modifier.padding(innerPadding), + ) + } +} + +@Composable +@PreviewLightDark +internal fun SettingsRootPreview() { + PhyphoxTheme { + SettingsRoot(uiState = SettingsUiState()) + } +} + + + +@Composable +fun ListHeaderItem( + modifier: Modifier = Modifier, + text: StringUIModel, +) { + Text( + modifier = modifier, + text = text.resolve(), + ) +} + +@PreviewLightDark +@Composable +private fun ListHeaderItemPreview() { + PhyphoxTheme { + ListHeaderItem(text = LoremIpsum(2).values.first().toStringUIModel()) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt new file mode 100644 index 00000000..6b8775b3 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt @@ -0,0 +1,58 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.composeable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +@Composable +fun SwitchPreferenceItem( + title: String, + summary: String? = null, + iconRes: Int? = null, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onCheckedChange(!checked) } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (iconRes != null) { + Icon( + painter = painterResource(id = iconRes), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.width(16.dp)) + } + Column(modifier = Modifier.weight(1f)) { + Text(text = title, style = MaterialTheme.typography.bodyLarge) + if (summary != null) { + Text( + text = summary, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + Switch(checked = checked, onCheckedChange = onCheckedChange) + } +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt new file mode 100644 index 00000000..43e2da30 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -0,0 +1,15 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class SettingsUiState( + val currentLanguage: ResourceState = ResourceState.Loading, + val graphSize: ResourceState = ResourceState.Loading, + val accessPort: ResourceState = ResourceState.Loading, + val proximityLockEnabled: ResourceState = ResourceState.Loading, +) + +sealed interface ResourceState { + data object Loading : ResourceState + data class Success(val data: T) : ResourceState +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 19725e8e..c7915c7a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -4,9 +4,10 @@ import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject @HiltViewModel -class SettingsViewModel : ViewModel() { +class SettingsViewModel @Inject constructor(): ViewModel() { private val _uiState = MutableStateFlow(SettingsUiState()) val uiState = _uiState.asStateFlow() From a9b4fb86690ff0b57afb2b9bdaddc27abea92c46 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 22:14:30 +0100 Subject: [PATCH 017/111] add skeleton loading extension --- .../phyphox/ui/ModifierExtensions.kt | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt new file mode 100644 index 00000000..1374a0ca --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt @@ -0,0 +1,76 @@ +package de.rwth_aachen.phyphox.ui + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.dp + +/** + * Extension to add a skeleton loading effect (shimmer) to a Composable. + */ +fun Modifier.skeleton( + show: Boolean = true, + shape: Shape = RoundedCornerShape(4.dp), +): Modifier = if (show) { + composed { + val transition = rememberInfiniteTransition(label = "skeleton") + val translateAnimation by transition.animateFloat( + initialValue = 0f, + targetValue = 1000f, + animationSpec = infiniteRepeatable( + animation = tween( + durationMillis = 1000, + easing = LinearEasing, + ), + repeatMode = RepeatMode.Restart, + ), + label = "shimmer", + ) + + val shimmerColors = listOf( + MaterialTheme.colorScheme.surfaceVariant, + MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.1f), + MaterialTheme.colorScheme.surfaceVariant, + ) + + val brush = Brush.linearGradient( + colors = shimmerColors, + start = Offset(translateAnimation - 500f, translateAnimation - 500f), + end = Offset(translateAnimation, translateAnimation), + ) + + background(brush, shape) + } +} else { + this +} + +/** + * Marks a view as loading. When [isLoading] is true, the content is hidden and a skeleton + * shimmer is displayed in its place, preserving the size of the Composable. + */ +fun Modifier.placeholder( + isLoading: Boolean, + shape: Shape = RoundedCornerShape(4.dp), +): Modifier = if (isLoading) { + this + .drawWithContent { + // Do not draw the content + } + .skeleton(show = true, shape = shape) +} else { + this +} From a94e09e139bab54e4d6168e6c5bc60487fef5f97 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 22:32:06 +0100 Subject: [PATCH 018/111] Add LoremIpsumStringUIModel.kt --- .../phyphox/ui/string/LoremIpsumStringUIModel.kt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/ui/string/LoremIpsumStringUIModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/LoremIpsumStringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/LoremIpsumStringUIModel.kt new file mode 100644 index 00000000..76c475f7 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/LoremIpsumStringUIModel.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.ui.string + +import android.content.Context +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum + +class LoremIpsumStringUIModel(val wordCount: Int = 500) : StringUIModel() { + override fun resolve(context: Context): String = LoremIpsum(wordCount).values.first() +} From 1dff7b5803b86037042dda24b236d4ecb719fb8f Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 22:32:25 +0100 Subject: [PATCH 019/111] change package for TextStringUIModel.kt --- .../java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt index b6abdf86..55e37d4f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/TextStringUIModel.kt @@ -1,7 +1,6 @@ -package shresthajibesh.cookiejar.lib.text +package de.rwth_aachen.phyphox.ui.string import android.content.Context -import de.rwth_aachen.phyphox.ui.string.StringUIModel class TextStringUIModel(val value: String) : StringUIModel() { override fun resolve(context: Context): String = value From 3660c9c460eef37aa07fc9c83c83282a3ec537ff Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 22:32:40 +0100 Subject: [PATCH 020/111] move items --- .../settings/presentation/SettingsActivity.kt | 13 +- .../{composeable => compose}/SettingsRoot.kt | 38 ++---- .../PreferenceCategoryHeader.kt} | 4 +- .../preferenceitem}/PreferenceItem.kt | 25 +++- .../SeekBarPreferenceItem.kt | 2 +- .../settingscontent/SettingsContent.kt | 129 ++++++++++++++++++ .../SwitchPreferenceItem.kt | 55 ++++++-- .../composeable/SettingsContent.kt | 99 -------------- .../presentation/viewmodel/SettingsUiState.kt | 15 +- .../phyphox/ui/string/StringUIModelExt.kt | 1 - 10 files changed, 228 insertions(+), 153 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable => compose}/SettingsRoot.kt (65%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable/PreferenceCategory.kt => compose/preferencecategoryheader/PreferenceCategoryHeader.kt} (89%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable => compose/preferenceitem}/PreferenceItem.kt (68%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable => compose/seekbarpreferenceitem}/SeekBarPreferenceItem.kt (97%) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{composeable => compose/switchpreferenceitem}/SwitchPreferenceItem.kt (50%) delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index d78a78a1..37846edb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -5,11 +5,11 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels +import androidx.compose.material3.Surface import androidx.compose.runtime.getValue -import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint -import de.rwth_aachen.phyphox.features.settings.presentation.composeable.SettingsRoot +import de.rwth_aachen.phyphox.features.settings.presentation.compose.SettingsRoot import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -23,9 +23,12 @@ class SettingsActivity : ComponentActivity() { setContent { PhyphoxTheme { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - SettingsRoot( - uiState = uiState - ) + Surface { + SettingsRoot( + uiState = uiState, + ) + } + } } // setContentView(R.layout.activity_settings) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt similarity index 65% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 7f31af22..14fff45b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -1,6 +1,5 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable +package de.rwth_aachen.phyphox.features.settings.presentation.compose -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold @@ -10,12 +9,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark -import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent.SettingsContent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState -import de.rwth_aachen.phyphox.ui.string.StringUIModel -import de.rwth_aachen.phyphox.ui.string.resolve -import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @OptIn(ExperimentalMaterial3Api::class) @@ -27,13 +23,18 @@ fun SettingsRoot( Scaffold( topBar = { TopAppBar( - title = { Text(stringResource(id = R.string.action_settings)) } + title = { Text(stringResource(id = R.string.action_settings)) }, ) }, - modifier = modifier + modifier = modifier, ) { innerPadding -> SettingsContent( modifier = modifier.padding(innerPadding), + currentLanguage = uiState.currentLanguage, + graphSize = uiState.graphSize, + uiMode = uiState.uiMode, + accessPort = uiState.accessPort, + proximityLockEnabled = uiState.proximityLockEnabled, ) } } @@ -45,24 +46,3 @@ internal fun SettingsRootPreview() { SettingsRoot(uiState = SettingsUiState()) } } - - - -@Composable -fun ListHeaderItem( - modifier: Modifier = Modifier, - text: StringUIModel, -) { - Text( - modifier = modifier, - text = text.resolve(), - ) -} - -@PreviewLightDark -@Composable -private fun ListHeaderItemPreview() { - PhyphoxTheme { - ListHeaderItem(text = LoremIpsum(2).values.first().toStringUIModel()) - } -} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt similarity index 89% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt index 38fa1d20..5d84b815 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceCategory.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @Composable -fun PreferenceCategory(title: String) { +fun PreferenceCategoryHeader(title: String) { Text( text = title, style = MaterialTheme.typography.labelLarge, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt similarity index 68% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt index 18dbc91a..5af1c3a9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt @@ -1,10 +1,12 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -16,11 +18,15 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.ui.skeleton +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve @Composable fun PreferenceItem( title: String, - summary: String? = null, + summary: ResourceState, iconRes: Int? = null, onClick: () -> Unit = {}, ) { @@ -42,9 +48,18 @@ fun PreferenceItem( } Column(modifier = Modifier.weight(1f)) { Text(text = title, style = MaterialTheme.typography.bodyLarge) - if (summary != null) { - Text( - text = summary, + + when (summary) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 4.dp) + .width(120.dp) + .height(16.dp) + .skeleton(), + ) + + is ResourceState.Success -> Text( + text = summary.data.resolve(), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt similarity index 97% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index de16cce9..18ce4b96 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable +package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt new file mode 100644 index 00000000..457d5edd --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -0,0 +1,129 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent + +import android.content.res.Configuration +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.GraphSizeUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SettingsContent( + modifier: Modifier = Modifier, + currentLanguage: ResourceState, + graphSize: ResourceState, + uiMode: ResourceState, + accessPort: ResourceState, + proximityLockEnabled: ResourceState, +) { + LazyColumn( + modifier = modifier + .fillMaxSize(), + ) { + // Language Category + item { + PreferenceCategoryHeader( + title = stringResource(id = R.string.settingsHeadLanguage), + ) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsLanguage), + summary = currentLanguage, + iconRes = R.drawable.setting_language, + ) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsTranslation), + summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), + iconRes = R.drawable.setting_translate, + ) + } + + // Graph View Category + item { + PreferenceCategoryHeader(title = stringResource(id = R.string.settingGraphViewEdit)) + } + item { + var graphSize by remember { mutableFloatStateOf(1f) } + SeekBarPreferenceItem( + title = stringResource(R.string.settingGraphSize), + value = graphSize, + valueRange = 0f..3f, + onValueChange = { graphSize = it }, + ) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settings_theme_title), + summary = uiMode, + iconRes = R.drawable.ic_dark_mode, + ) + } + + // Advanced Category + item { + PreferenceCategoryHeader(title = stringResource(id = R.string.settingsHeadAdvanced)) + } + item { + PreferenceItem( + title = stringResource(id = R.string.settingsPort), + summary = accessPort, + iconRes = R.drawable.setting_http, + ) + } + item { + SwitchPreferenceItem( + title = ResourceStringUIModel(resId = R.string.settingsProximityLock), + summary = ResourceStringUIModel(resId = R.string.settingsProximityLockDetail), + iconRes = R.drawable.setting_lock, + checked = proximityLockEnabled, + onCheckedChange = { }, + ) + } + } + +} + + +@Preview( + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_YES, +) +@Preview( + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_NO, +) +@Composable +fun SettingsContentPreview() { + PhyphoxTheme { + Surface { + SettingsContent( + currentLanguage = ResourceState.Loading, + graphSize = ResourceState.Loading, + uiMode = ResourceState.Loading, + accessPort = ResourceState.Loading, + proximityLockEnabled = ResourceState.Loading, + ) + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt similarity index 50% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index 6b8775b3..b811901d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -1,5 +1,6 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable +package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem +import android.content.res.Configuration import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -10,30 +11,44 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SwitchPreferenceItem( - title: String, - summary: String? = null, + title: StringUIModel, + summary: StringUIModel? = null, iconRes: Int? = null, - checked: Boolean, + checked: ResourceState, onCheckedChange: (Boolean) -> Unit, ) { + val isClickable = checked is ResourceState.Success + Row( modifier = Modifier .fillMaxWidth() - .clickable { onCheckedChange(!checked) } + .clickable(isClickable) { + + onCheckedChange(checked.isChecked()) + } .padding(16.dp), verticalAlignment = Alignment.CenterVertically, ) { - if (iconRes != null) { + iconRes?.let { Icon( painter = painterResource(id = iconRes), contentDescription = null, @@ -43,16 +58,38 @@ fun SwitchPreferenceItem( Spacer(modifier = Modifier.width(16.dp)) } Column(modifier = Modifier.weight(1f)) { - Text(text = title, style = MaterialTheme.typography.bodyLarge) + Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) if (summary != null) { Text( - text = summary, + text = summary.resolve(), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } - Switch(checked = checked, onCheckedChange = onCheckedChange) + Switch(checked = checked.isChecked(), onCheckedChange = onCheckedChange) } } +@Preview( + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_YES, +) +@Preview( + showBackground = true, + uiMode = Configuration.UI_MODE_NIGHT_NO, +) +@Composable +internal fun SwitchPreferenceItemPreview(){ + PhyphoxTheme { + Surface { + SwitchPreferenceItem( + title = LoremIpsumStringUIModel(2), + summary= LoremIpsumStringUIModel(15), + iconRes = R.drawable.ic_dark_mode, + checked = ResourceState.Success(true), + onCheckedChange = {}, + ) + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt deleted file mode 100644 index 05df4705..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/composeable/SettingsContent.kt +++ /dev/null @@ -1,99 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.composeable - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun SettingsContent( - modifier: Modifier = Modifier, -) { - LazyColumn( - modifier = modifier - .fillMaxSize(), - ) { - // Language Category - item { - PreferenceCategory(title = stringResource(id = R.string.settingsHeadLanguage)) - } - item { - PreferenceItem( - title = stringResource(id = R.string.settingsLanguage), - summary = "English", // Mock summary - iconRes = R.drawable.setting_language, - ) - } - item { - PreferenceItem( - title = stringResource(id = R.string.settingsTranslation), - summary = stringResource(id = R.string.settingsTranslationMore), - iconRes = R.drawable.setting_translate, - ) - } - - // Graph View Category - item { - PreferenceCategory(title = stringResource(id = R.string.settingGraphViewEdit)) - } - item { - var graphSize by remember { mutableFloatStateOf(1f) } - SeekBarPreferenceItem( - title = "Graph size", // Mock title - value = graphSize, - valueRange = 0f..3f, - onValueChange = { graphSize = it }, - ) - } - item { - PreferenceItem( - title = stringResource(id = R.string.settings_theme_title), - summary = stringResource(id = R.string.settings_mode_dark_system), // Mock summary - iconRes = R.drawable.ic_dark_mode, - ) - } - - // Advanced Category - item { - PreferenceCategory(title = stringResource(id = R.string.settingsHeadAdvanced)) - } - item { - PreferenceItem( - title = stringResource(id = R.string.settingsPort), - summary = "8080", - iconRes = R.drawable.setting_http, - ) - } - item { - var proximityLock by remember { mutableStateOf(false) } - SwitchPreferenceItem( - title = stringResource(id = R.string.settingsProximityLock), - summary = stringResource(id = R.string.settingsProximityLockDetail), - iconRes = R.drawable.setting_lock, - checked = proximityLock, - onCheckedChange = { proximityLock = it }, - ) - } - } - -} - - -@Preview(showBackground = true) -@Composable -fun SettingsContentPreview() { - PhyphoxTheme { - SettingsContent() - } -} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 43e2da30..e3dca4a5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -4,8 +4,9 @@ import de.rwth_aachen.phyphox.ui.string.StringUIModel data class SettingsUiState( val currentLanguage: ResourceState = ResourceState.Loading, - val graphSize: ResourceState = ResourceState.Loading, - val accessPort: ResourceState = ResourceState.Loading, + val graphSize: ResourceState = ResourceState.Loading, + val uiMode: ResourceState = ResourceState.Loading, + val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, ) @@ -13,3 +14,13 @@ sealed interface ResourceState { data object Loading : ResourceState data class Success(val data: T) : ResourceState } + +fun ResourceState.isChecked(): Boolean { + return this is ResourceState.Success && this.data +} + +data class GraphSizeUiModel( + val currentSize: Float, + val minSize: Float, + val maxSize: Float, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt index f2b71e34..425882c4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt @@ -3,7 +3,6 @@ package de.rwth_aachen.phyphox.ui.string import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext -import shresthajibesh.cookiejar.lib.text.TextStringUIModel val emptyStringUiModel = "".toStringUIModel() From c29bfabc00e7c57014fb7c320273a6b78ef7027a Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:12:59 +0100 Subject: [PATCH 021/111] add preview providers --- .../presentation/compose/SettingsRoot.kt | 2 +- .../PreferenceCategoryHeader.kt | 29 ++++++++- ...PreferenceCategoryHeaderPreviewProvider.kt | 15 +++++ .../compose/preferenceitem/PreferenceItem.kt | 26 +++++++- .../PreferenceItemPreviewProvider.kt | 15 +++++ .../SeekBarPreferenceItem.kt | 60 +++++++++++++++---- .../SeekBarPreferenceItemPreviewProvider.kt | 19 ++++++ .../settingscontent/SettingsContent.kt | 35 +++++------ .../SwitchPreferenceItem.kt | 37 +++++++++--- .../SwitchPreferenceItemPreviewProvider.kt | 13 ++++ .../presentation/viewmodel/SettingsUiState.kt | 18 ++++-- 11 files changed, 218 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 14fff45b..e292219d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -31,7 +31,7 @@ fun SettingsRoot( SettingsContent( modifier = modifier.padding(innerPadding), currentLanguage = uiState.currentLanguage, - graphSize = uiState.graphSize, + seekbarConfig = uiState.graphSize, uiMode = uiState.uiMode, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt index 5d84b815..fc7fbbf1 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt @@ -3,19 +3,42 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.preference import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable -fun PreferenceCategoryHeader(title: String) { +fun PreferenceCategoryHeader( + modifier: Modifier = Modifier, + title: StringUIModel, +) { Text( - text = title, + text = title.resolve(), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, - modifier = Modifier + modifier = modifier .fillMaxWidth() .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp), ) } + +@Preview(showBackground = true) +@Composable +internal fun SeekBarPreferenceItemPreview( + @PreviewParameter(PreferenceCategoryHeaderPreviewProvider::class) preview: StringUIModel, +) { + PhyphoxTheme { + Surface { + PreferenceCategoryHeader( + title = preview, + ) + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt new file mode 100644 index 00000000..d27992cb --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt @@ -0,0 +1,15 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +class PreferenceCategoryHeaderPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + LoremIpsumStringUIModel(8), + ) +} + + + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt index 5af1c3a9..46a2d666 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt @@ -12,20 +12,25 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.skeleton +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun PreferenceItem( - title: String, + title: StringUIModel, summary: ResourceState, iconRes: Int? = null, onClick: () -> Unit = {}, @@ -47,13 +52,13 @@ fun PreferenceItem( Spacer(modifier = Modifier.width(16.dp)) } Column(modifier = Modifier.weight(1f)) { - Text(text = title, style = MaterialTheme.typography.bodyLarge) + Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) when (summary) { ResourceState.Loading -> Box( modifier = Modifier .padding(top = 4.dp) - .width(120.dp) + .fillMaxWidth() .height(16.dp) .skeleton(), ) @@ -67,3 +72,18 @@ fun PreferenceItem( } } } + +@Preview(showBackground = true) +@Composable +internal fun SeekBarPreferenceItemPreview( + @PreviewParameter(PreferenceItemPreviewProvider::class) preview: ResourceState, +) { + PhyphoxTheme { + Surface { + PreferenceItem( + title = LoremIpsumStringUIModel(2), + summary = preview, + ) + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt new file mode 100644 index 00000000..562050a0 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt @@ -0,0 +1,15 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +class PreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> + get() = sequenceOf( + ResourceState.Loading, + ResourceState.Success(LoremIpsumStringUIModel(8)), + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 18ce4b96..6740604c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -1,20 +1,32 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import de.rwth_aachen.phyphox.ui.skeleton +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SeekBarPreferenceItem( - title: String, - value: Float, - valueRange: ClosedFloatingPointRange, + title: StringUIModel, + seekBarConfig: ResourceState, onValueChange: (Float) -> Unit, ) { Column( @@ -22,12 +34,40 @@ fun SeekBarPreferenceItem( .fillMaxWidth() .padding(16.dp), ) { - Text(text = title, style = MaterialTheme.typography.bodyLarge) - Slider( - value = value, - onValueChange = onValueChange, - valueRange = valueRange, - steps = (valueRange.endInclusive - valueRange.start).toInt() - 1, - ) + Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) + + when (seekBarConfig) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() + .height(16.dp) + .skeleton(), + ) + + is ResourceState.Success -> Slider( + value = seekBarConfig.data.currentSize, + onValueChange = onValueChange, + valueRange = seekBarConfig.data.range, + steps = (seekBarConfig.data.range.endInclusive - seekBarConfig.data.range.start).toInt() - 1, + ) + } + + } +} + +@Preview(showBackground = true) +@Composable +internal fun SeekBarPreferenceItemPreview( + @PreviewParameter(SeekBarPreferenceItemPreviewProvider::class) graphSize: ResourceState, +) { + PhyphoxTheme { + Surface { + SeekBarPreferenceItem( + title = LoremIpsumStringUIModel(2), + seekBarConfig = graphSize, + onValueChange = {}, + ) + } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt new file mode 100644 index 00000000..709b6d4e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt @@ -0,0 +1,19 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState + +class SeekBarPreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> + get() = sequenceOf( + ResourceState.Loading, + ResourceState.Success( + SeekBarConfig( + currentSize = 1f, + minSize = 0f, + maxSize = 3f, + ), + ), + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 457d5edd..37b245db 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -6,20 +6,15 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.GraphSizeUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -29,7 +24,7 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme fun SettingsContent( modifier: Modifier = Modifier, currentLanguage: ResourceState, - graphSize: ResourceState, + seekbarConfig: ResourceState, uiMode: ResourceState, accessPort: ResourceState, proximityLockEnabled: ResourceState, @@ -41,19 +36,19 @@ fun SettingsContent( // Language Category item { PreferenceCategoryHeader( - title = stringResource(id = R.string.settingsHeadLanguage), + title = ResourceStringUIModel(resId = R.string.settingsHeadLanguage), ) } item { PreferenceItem( - title = stringResource(id = R.string.settingsLanguage), + title = ResourceStringUIModel(resId = R.string.settingsLanguage), summary = currentLanguage, iconRes = R.drawable.setting_language, ) } item { PreferenceItem( - title = stringResource(id = R.string.settingsTranslation), + title = ResourceStringUIModel(resId = R.string.settingsTranslation), summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), iconRes = R.drawable.setting_translate, ) @@ -61,20 +56,20 @@ fun SettingsContent( // Graph View Category item { - PreferenceCategoryHeader(title = stringResource(id = R.string.settingGraphViewEdit)) + PreferenceCategoryHeader( + title = ResourceStringUIModel(resId = R.string.settingGraphViewEdit), + ) } item { - var graphSize by remember { mutableFloatStateOf(1f) } SeekBarPreferenceItem( - title = stringResource(R.string.settingGraphSize), - value = graphSize, - valueRange = 0f..3f, - onValueChange = { graphSize = it }, + title = ResourceStringUIModel(R.string.settingGraphSize), + seekBarConfig = seekbarConfig, + onValueChange = { }, ) } item { PreferenceItem( - title = stringResource(id = R.string.settings_theme_title), + title = ResourceStringUIModel(resId = R.string.settings_theme_title), summary = uiMode, iconRes = R.drawable.ic_dark_mode, ) @@ -82,11 +77,11 @@ fun SettingsContent( // Advanced Category item { - PreferenceCategoryHeader(title = stringResource(id = R.string.settingsHeadAdvanced)) + PreferenceCategoryHeader(title = ResourceStringUIModel(resId = R.string.settingsHeadAdvanced)) } item { PreferenceItem( - title = stringResource(id = R.string.settingsPort), + title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, iconRes = R.drawable.setting_http, ) @@ -119,7 +114,7 @@ fun SettingsContentPreview() { Surface { SettingsContent( currentLanguage = ResourceState.Loading, - graphSize = ResourceState.Loading, + seekbarConfig = ResourceState.Loading, uiMode = ResourceState.Loading, accessPort = ResourceState.Loading, proximityLockEnabled = ResourceState.Loading, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index b811901d..49886c40 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -2,10 +2,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpref import android.content.res.Configuration import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -19,10 +21,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked +import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve @@ -36,13 +40,10 @@ fun SwitchPreferenceItem( checked: ResourceState, onCheckedChange: (Boolean) -> Unit, ) { - val isClickable = checked is ResourceState.Success - Row( modifier = Modifier .fillMaxWidth() - .clickable(isClickable) { - + .clickable(checked is ResourceState.Success) { onCheckedChange(checked.isChecked()) } .padding(16.dp), @@ -59,7 +60,7 @@ fun SwitchPreferenceItem( } Column(modifier = Modifier.weight(1f)) { Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) - if (summary != null) { + summary?.let { Text( text = summary.resolve(), style = MaterialTheme.typography.bodyMedium, @@ -67,7 +68,23 @@ fun SwitchPreferenceItem( ) } } - Switch(checked = checked.isChecked(), onCheckedChange = onCheckedChange) + Spacer(Modifier.size(4.dp)) + when (checked) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 16.dp) + .width(48.dp) + .height(32.dp) + .skeleton(), + ) + + is ResourceState.Success -> + Switch( + checked = checked.isChecked(), + onCheckedChange = onCheckedChange, + ) + } + } } @@ -80,14 +97,16 @@ fun SwitchPreferenceItem( uiMode = Configuration.UI_MODE_NIGHT_NO, ) @Composable -internal fun SwitchPreferenceItemPreview(){ +internal fun SwitchPreferenceItemPreview( + @PreviewParameter(SwitchPreferenceItemPreviewProvider::class) value: ResourceState, +) { PhyphoxTheme { Surface { SwitchPreferenceItem( title = LoremIpsumStringUIModel(2), - summary= LoremIpsumStringUIModel(15), + summary = LoremIpsumStringUIModel(15), iconRes = R.drawable.ic_dark_mode, - checked = ResourceState.Success(true), + checked = value, onCheckedChange = {}, ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt new file mode 100644 index 00000000..4d337054 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt @@ -0,0 +1,13 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState + +class SwitchPreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> + get() = sequenceOf( + ResourceState.Loading, + ResourceState.Success(true), + ResourceState.Success(false), + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index e3dca4a5..574b1015 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -4,7 +4,7 @@ import de.rwth_aachen.phyphox.ui.string.StringUIModel data class SettingsUiState( val currentLanguage: ResourceState = ResourceState.Loading, - val graphSize: ResourceState = ResourceState.Loading, + val graphSize: ResourceState = ResourceState.Loading, val uiMode: ResourceState = ResourceState.Loading, val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, @@ -19,8 +19,16 @@ fun ResourceState.isChecked(): Boolean { return this is ResourceState.Success && this.data } -data class GraphSizeUiModel( +data class SeekBarConfig( val currentSize: Float, - val minSize: Float, - val maxSize: Float, -) + val range: ClosedFloatingPointRange, +) { + constructor( + currentSize: Float, + minSize: Float, + maxSize: Float, + ): this( + currentSize = currentSize, + range = minSize..maxSize, + ) +} From b1986181c774782d08b7a30d3d8775c78bd14d03 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:15:01 +0100 Subject: [PATCH 022/111] update spacing --- .../compose/switchpreferenceitem/SwitchPreferenceItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index 49886c40..250e5836 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -68,7 +68,7 @@ fun SwitchPreferenceItem( ) } } - Spacer(Modifier.size(4.dp)) + Spacer(Modifier.size(8.dp)) when (checked) { ResourceState.Loading -> Box( modifier = Modifier From 94049126014e9f2acd4990b718e944611ac340c9 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:20:34 +0100 Subject: [PATCH 023/111] add and comment out dynamic theme option in settings ui state --- .../features/settings/presentation/viewmodel/SettingsUiState.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 574b1015..6ee91d9e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -6,8 +6,10 @@ data class SettingsUiState( val currentLanguage: ResourceState = ResourceState.Loading, val graphSize: ResourceState = ResourceState.Loading, val uiMode: ResourceState = ResourceState.Loading, +// val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, + ) sealed interface ResourceState { From ebbd761425300ea77215a93bb54b247ca6b841e3 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:26:20 +0100 Subject: [PATCH 024/111] handle back navigation --- .../settings/presentation/SettingsActivity.kt | 51 ++----------------- .../presentation/compose/SettingsRoot.kt | 13 +++++ app/src/main/res/values/strings.xml | 1 + 3 files changed, 18 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 37846edb..511dc526 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -2,6 +2,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels @@ -22,60 +23,16 @@ class SettingsActivity : ComponentActivity() { this.enableEdgeToEdge() setContent { PhyphoxTheme { - val uiState by viewModel.uiState.collectAsStateWithLifecycle() Surface { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher SettingsRoot( uiState = uiState, + onBackClick = { onBackPressedDispatcher?.onBackPressed() }, ) } } } -// setContentView(R.layout.activity_settings) -// inflateViews() -// setSupportActionBar(toolbar) -// updateActionBar() -// setFrameInsets() -// setToolBarInsets() -// showSettings() } - -// private fun inflateViews() { -// toolbar = findViewById(R.id.settingsToolbar) -// settingsFrame = findViewById(R.id.settingsFrame) -// } -// -// private fun updateActionBar() { -// supportActionBar?.apply { -// setDisplayHomeAsUpEnabled(true) -// setDisplayShowTitleEnabled(true) -// } -// } -// -// private fun showSettings() { -// supportFragmentManager -// .beginTransaction() -// .replace(R.id.settingsFrame, SettingsFragment()) -// .commit() -// } -// -// private fun setFrameInsets() { -// WindowInsetHelper.setInsets( -// settingsFrame, -// WindowInsetHelper.ApplyTo.PADDING, -// WindowInsetHelper.ApplyTo.IGNORE, -// WindowInsetHelper.ApplyTo.PADDING, -// WindowInsetHelper.ApplyTo.MARGIN, -// ) -// } -// -// private fun setToolBarInsets() { -// WindowInsetHelper.setInsets( -// toolbar, -// WindowInsetHelper.ApplyTo.PADDING, -// WindowInsetHelper.ApplyTo.PADDING, -// WindowInsetHelper.ApplyTo.PADDING, -// WindowInsetHelper.ApplyTo.IGNORE, -// ) -// } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index e292219d..b05b173d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -1,7 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -19,11 +23,20 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme fun SettingsRoot( modifier: Modifier = Modifier, uiState: SettingsUiState, + onBackClick: () -> Unit = {}, ) { Scaffold( topBar = { TopAppBar( title = { Text(stringResource(id = R.string.action_settings)) }, + navigationIcon = { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(id = R.string.back), + ) + } + }, ) }, modifier = modifier, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bf281c4c..99dc1874 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ phyphox https://phyphox.org/privacy Settings + Back phyphox Pan and zoom Pick data From 69f51ecae6b37efeb302a3a400516a3ccbddff3b Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:27:05 +0100 Subject: [PATCH 025/111] reduce spacer --- .../compose/switchpreferenceitem/SwitchPreferenceItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index 250e5836..49886c40 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -68,7 +68,7 @@ fun SwitchPreferenceItem( ) } } - Spacer(Modifier.size(8.dp)) + Spacer(Modifier.size(4.dp)) when (checked) { ResourceState.Loading -> Box( modifier = Modifier From 87d1ee9f7eb3a723407542ff665a69425225d30b Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:39:08 +0100 Subject: [PATCH 026/111] remove item wise padding in favour of universal settings content padding --- .../PreferenceCategoryHeader.kt | 3 +-- .../compose/preferenceitem/PreferenceItem.kt | 3 +-- .../SeekBarPreferenceItem.kt | 1 - .../compose/settingscontent/SettingsContent.kt | 15 ++++++++++++++- .../switchpreferenceitem/SwitchPreferenceItem.kt | 3 +-- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt index fc7fbbf1..c7a41090 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt @@ -24,8 +24,7 @@ fun PreferenceCategoryHeader( style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.primary, modifier = modifier - .fillMaxWidth() - .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 8.dp), + .fillMaxWidth(), ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt index 46a2d666..3af27bb3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt @@ -38,8 +38,7 @@ fun PreferenceItem( Row( modifier = Modifier .fillMaxWidth() - .clickable(onClick = onClick) - .padding(16.dp), + .clickable(onClick = onClick), verticalAlignment = Alignment.CenterVertically, ) { if (iconRes != null) { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 6740604c..e7228df1 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -32,7 +32,6 @@ fun SeekBarPreferenceItem( Column( modifier = Modifier .fillMaxWidth() - .padding(16.dp), ) { Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 37b245db..97b4aa56 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -1,13 +1,18 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent import android.content.res.Configuration +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Surface import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem @@ -32,6 +37,9 @@ fun SettingsContent( LazyColumn( modifier = modifier .fillMaxSize(), + contentPadding = PaddingValues(16.dp), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(16.dp) ) { // Language Category item { @@ -54,6 +62,9 @@ fun SettingsContent( ) } + item { + HorizontalDivider() + } // Graph View Category item { PreferenceCategoryHeader( @@ -74,7 +85,9 @@ fun SettingsContent( iconRes = R.drawable.ic_dark_mode, ) } - + item { + HorizontalDivider() + } // Advanced Category item { PreferenceCategoryHeader(title = ResourceStringUIModel(resId = R.string.settingsHeadAdvanced)) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index 49886c40..b6dd722b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -45,8 +45,7 @@ fun SwitchPreferenceItem( .fillMaxWidth() .clickable(checked is ResourceState.Success) { onCheckedChange(checked.isChecked()) - } - .padding(16.dp), + }, verticalAlignment = Alignment.CenterVertically, ) { iconRes?.let { From 223844c5a25fa6f9f54991c44d39335662db4b72 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:48:17 +0100 Subject: [PATCH 027/111] remove item wise padding in favour of universal settings content padding --- .../settingscontent/SettingsContent.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 97b4aa56..088a08cc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -33,13 +33,19 @@ fun SettingsContent( uiMode: ResourceState, accessPort: ResourceState, proximityLockEnabled: ResourceState, + onAppLanguageClicked: () -> Unit, + onLearnMoreAboutTranslationClicked: () -> Unit, + onGraphSizeChanged: (Float) -> Unit, + onUiModeClicked: () -> Unit, + onAccessPortClicked: () -> Unit, + onProximityLockChanged: (Boolean) -> Unit, ) { LazyColumn( modifier = modifier .fillMaxSize(), contentPadding = PaddingValues(16.dp), horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(16.dp) + verticalArrangement = Arrangement.spacedBy(16.dp), ) { // Language Category item { @@ -52,6 +58,7 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsLanguage), summary = currentLanguage, iconRes = R.drawable.setting_language, + onClick = onAppLanguageClicked, ) } item { @@ -59,6 +66,7 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsTranslation), summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), iconRes = R.drawable.setting_translate, + onClick = onLearnMoreAboutTranslationClicked, ) } @@ -75,7 +83,7 @@ fun SettingsContent( SeekBarPreferenceItem( title = ResourceStringUIModel(R.string.settingGraphSize), seekBarConfig = seekbarConfig, - onValueChange = { }, + onValueChange = onGraphSizeChanged, ) } item { @@ -83,6 +91,7 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settings_theme_title), summary = uiMode, iconRes = R.drawable.ic_dark_mode, + onClick = onUiModeClicked ) } item { @@ -97,6 +106,7 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, iconRes = R.drawable.setting_http, + onClick = onAccessPortClicked, ) } item { @@ -105,7 +115,7 @@ fun SettingsContent( summary = ResourceStringUIModel(resId = R.string.settingsProximityLockDetail), iconRes = R.drawable.setting_lock, checked = proximityLockEnabled, - onCheckedChange = { }, + onCheckedChange = onProximityLockChanged, ) } } @@ -131,6 +141,12 @@ fun SettingsContentPreview() { uiMode = ResourceState.Loading, accessPort = ResourceState.Loading, proximityLockEnabled = ResourceState.Loading, + onAppLanguageClicked = {}, + onLearnMoreAboutTranslationClicked = {}, + onGraphSizeChanged = {}, + onUiModeClicked = {}, + onAccessPortClicked = {}, + onProximityLockChanged = {}, ) } } From 135da84afa9ca2076d54ae1da31d10c99f4bc2b2 Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:49:51 +0100 Subject: [PATCH 028/111] remove item wise padding in favour of universal settings content padding --- .../presentation/compose/settingscontent/SettingsContent.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 088a08cc..99f34a69 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -47,7 +47,8 @@ fun SettingsContent( horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(16.dp), ) { - // Language Category + //TODO: Once the experiment list has been migrated to compose + // move the logo from top bar in experiment list to here item { PreferenceCategoryHeader( title = ResourceStringUIModel(resId = R.string.settingsHeadLanguage), From 81375e9d3e489752cd9330c97b1a7dba16a083fe Mon Sep 17 00:00:00 2001 From: owl Date: Wed, 31 Dec 2025 23:59:55 +0100 Subject: [PATCH 029/111] pass events --- .../settings/presentation/SettingsActivity.kt | 6 +++++ .../presentation/compose/SettingsRoot.kt | 22 ++++++++++++++++++- .../settingscontent/SettingsContent.kt | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 511dc526..1bc03f14 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -29,6 +29,12 @@ class SettingsActivity : ComponentActivity() { SettingsRoot( uiState = uiState, onBackClick = { onBackPressedDispatcher?.onBackPressed() }, + onAppLanguageClicked = {}, + onLearnMoreAboutTranslationClicked = {}, + onGraphSizeChanged = {}, + onUiModeClicked = {}, + onAccessPortClicked = {}, + onProximityLockChanged = {}, ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index b05b173d..12d9d41e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -24,6 +24,12 @@ fun SettingsRoot( modifier: Modifier = Modifier, uiState: SettingsUiState, onBackClick: () -> Unit = {}, + onAppLanguageClicked: () -> Unit, + onLearnMoreAboutTranslationClicked: () -> Unit, + onGraphSizeChanged: (Float) -> Unit, + onUiModeClicked: () -> Unit, + onAccessPortClicked: () -> Unit, + onProximityLockChanged: (Boolean) -> Unit, ) { Scaffold( topBar = { @@ -48,6 +54,12 @@ fun SettingsRoot( uiMode = uiState.uiMode, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, + onAppLanguageClicked = onAppLanguageClicked, + onLearnMoreAboutTranslationClicked = onLearnMoreAboutTranslationClicked, + onGraphSizeChanged = onGraphSizeChanged, + onUiModeClicked = onUiModeClicked, + onAccessPortClicked = onAccessPortClicked, + onProximityLockChanged = onProximityLockChanged, ) } } @@ -56,6 +68,14 @@ fun SettingsRoot( @PreviewLightDark internal fun SettingsRootPreview() { PhyphoxTheme { - SettingsRoot(uiState = SettingsUiState()) + SettingsRoot( + uiState = SettingsUiState(), + onAppLanguageClicked = {}, + onLearnMoreAboutTranslationClicked = {}, + onGraphSizeChanged = {}, + onUiModeClicked = {}, + onAccessPortClicked = {}, + onProximityLockChanged = {}, + ) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 99f34a69..ba25ed49 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -103,6 +103,7 @@ fun SettingsContent( PreferenceCategoryHeader(title = ResourceStringUIModel(resId = R.string.settingsHeadAdvanced)) } item { + //replace this with SingleChoiceSegmentedButtonRow PreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, From d329e994a774063be7a54212421c7a441123e7ac Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 00:02:24 +0100 Subject: [PATCH 030/111] address some lint issues --- .../PreferenceCategoryHeader.kt | 2 -- .../preferenceitem/PreferenceItemPreviewProvider.kt | 1 - .../seekbarpreferenceitem/SeekBarPreferenceItem.kt | 3 +-- .../phyphox/ui/string/StringUIModelExt.kt | 12 +++++++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt index c7a41090..f16c1494 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt @@ -1,7 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -9,7 +8,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt index 562050a0..59099f57 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt @@ -1,7 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index e7228df1..40a7dff5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Surface @@ -31,7 +30,7 @@ fun SeekBarPreferenceItem( ) { Column( modifier = Modifier - .fillMaxWidth() + .fillMaxWidth(), ) { Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt index 425882c4..d90d81fc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModelExt.kt @@ -4,21 +4,27 @@ import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext +@Suppress("unused") val emptyStringUiModel = "".toStringUIModel() +@Suppress("unused") fun String.toStringUIModel(): StringUIModel = TextStringUIModel(this) + +@Suppress("unused") fun String?.toStringOrEmptyUIModel(): StringUIModel = this?.toStringUIModel() ?: emptyStringUiModel +@Suppress("unused") fun @receiver:StringRes Int.toStringUIModel( - vararg args: Any + vararg args: Any, ): StringUIModel = ResourceStringUIModel( resId = this, - formatArgs = args + formatArgs = args, ) +@Suppress("unused") @Composable fun StringUIModel.resolve() = resolve(LocalContext.current) - +@Suppress("unused") @Composable fun StringUIModel?.resolveOrDefault(default: String = ""): String = this?.resolve() ?: default From 44fc996183f27e60ebc4b3b0d246c51faa86d325 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 00:04:39 +0100 Subject: [PATCH 031/111] address some lint issues --- .../viewmodel/SettingsViewModel.kt | 3 +-- .../ui/string/ResourceStringUIModel.kt | 2 +- .../phyphox/ui/string/StringUIModel.kt | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index c7915c7a..9edf19bd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -7,9 +7,8 @@ import kotlinx.coroutines.flow.asStateFlow import javax.inject.Inject @HiltViewModel -class SettingsViewModel @Inject constructor(): ViewModel() { +class SettingsViewModel @Inject constructor() : ViewModel() { private val _uiState = MutableStateFlow(SettingsUiState()) val uiState = _uiState.asStateFlow() - } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt index 9573fd6e..11a5eace 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/ResourceStringUIModel.kt @@ -5,7 +5,7 @@ import androidx.annotation.StringRes class ResourceStringUIModel( @StringRes val resId: Int, - vararg val formatArgs: Any + vararg val formatArgs: Any, ) : StringUIModel() { override fun resolve(context: Context): String { return if (formatArgs.isEmpty()) { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt index 103901cf..dd526d54 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/string/StringUIModel.kt @@ -2,18 +2,19 @@ package de.rwth_aachen.phyphox.ui.string import android.content.Context - abstract class StringUIModel { abstract fun resolve(context: Context): String - protected fun resolveVarArgs(context: Context, args: Array): Array { + protected fun resolveVarArgs( + context: Context, + args: Array, + ): Array { return args.map { - if (it is StringUIModel) { - it.resolve(context) - } else { - it - } - }.toTypedArray() + if (it is StringUIModel) { + it.resolve(context) + } else { + it + } + }.toTypedArray() } } - From 1be20fc24cc3ecbb240bf2c6c7e7cbf096d78b2d Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 19:22:57 +0100 Subject: [PATCH 032/111] rename PreferenceItem.kt to ClickablePreferenceItem.kt --- ...{PreferenceItem.kt => ClickablePreferenceItem.kt} | 12 +++++++----- ....kt => ClickablePreferenceItemPreviewProvider.kt} | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/{PreferenceItem.kt => ClickablePreferenceItem.kt} (90%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/{PreferenceItemPreviewProvider.kt => ClickablePreferenceItemPreviewProvider.kt} (84%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt similarity index 90% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt index 3af27bb3..42b6da5f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt @@ -29,9 +29,9 @@ import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable -fun PreferenceItem( +fun ClickablePreferenceItem( title: StringUIModel, - summary: ResourceState, + summary: ResourceState?, iconRes: Int? = null, onClick: () -> Unit = {}, ) { @@ -67,6 +67,8 @@ fun PreferenceItem( style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) + + else -> Unit } } } @@ -74,12 +76,12 @@ fun PreferenceItem( @Preview(showBackground = true) @Composable -internal fun SeekBarPreferenceItemPreview( - @PreviewParameter(PreferenceItemPreviewProvider::class) preview: ResourceState, +internal fun SeekBarClickablePreferenceItemPreview( + @PreviewParameter(ClickablePreferenceItemPreviewProvider::class) preview: ResourceState, ) { PhyphoxTheme { Surface { - PreferenceItem( + ClickablePreferenceItem( title = LoremIpsumStringUIModel(2), summary = preview, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt similarity index 84% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt index 59099f57..e4c02cef 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt @@ -5,7 +5,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceS import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel -class PreferenceItemPreviewProvider : PreviewParameterProvider> { +class ClickablePreferenceItemPreviewProvider : PreviewParameterProvider> { override val values: Sequence> get() = sequenceOf( ResourceState.Loading, From bc100b5351e3dc6ba05f037f989e52a62335e691 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 19:41:04 +0100 Subject: [PATCH 033/111] create PreferenceItem.kt --- .../compose/preferenceitem/PreferenceItem.kt | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt new file mode 100644 index 00000000..dbcac3d6 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt @@ -0,0 +1,113 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.tooling.preview.datasource.LoremIpsum +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@Composable +fun PreferenceItem( + modifier: Modifier = Modifier, + title: StringUIModel, + iconRes: Int? = null, + defaultSpacing: Dp = 4.dp, + trailingContent: @Composable (RowScope.() -> Unit)? = null, + content: @Composable (ColumnScope.() -> Unit)? = null, +) { + Row( + modifier = modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(defaultSpacing), + ) { + iconRes?.let { + Icon( + painter = painterResource(id = iconRes), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.width(16.dp)) + } + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(defaultSpacing), + ) { + Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) + content?.let { content() } + } + trailingContent?.let { trailingContent() } + } +} + +@PreviewLightDark +@Composable +internal fun PreferenceItemPreview() { + PhyphoxTheme { + Surface { + PreferenceItem( + title = LoremIpsumStringUIModel(4), + iconRes = R.drawable.ic_dark_mode, + content = { + Text( + text = LoremIpsum(15).values.first(), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + ) + } + } +} + +@PreviewLightDark +@Composable +internal fun PreferenceItemWithTrailingItemPreview() { + PhyphoxTheme { + Surface { + PreferenceItem( + title = LoremIpsumStringUIModel(4), + iconRes = R.drawable.ic_dark_mode, + content = { + Text( + text = LoremIpsum(15).values.first(), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + trailingContent = { + Switch( + checked = true, + onCheckedChange = {}, + ) + }, + ) + } + } +} + From 123310c83e9aa590c5dc73cef810a4ec038bcd85 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 19:41:28 +0100 Subject: [PATCH 034/111] update ClickablePreferenceItem.kt --- .../ClickablePreferenceItem.kt | 47 ++++++------------- .../ClickablePreferenceItemPreviewProvider.kt | 2 +- 2 files changed, 16 insertions(+), 33 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{preferenceitem => clickablepreferenceitem}/ClickablePreferenceItem.kt (63%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{preferenceitem => clickablepreferenceitem}/ClickablePreferenceItemPreviewProvider.kt (95%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt similarity index 63% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt index 42b6da5f..6db6e10b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -1,26 +1,19 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel @@ -30,29 +23,21 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun ClickablePreferenceItem( + modifier: Modifier = Modifier, title: StringUIModel, - summary: ResourceState?, + summary: ResourceState, iconRes: Int? = null, onClick: () -> Unit = {}, ) { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onClick), - verticalAlignment = Alignment.CenterVertically, - ) { - if (iconRes != null) { - Icon( - painter = painterResource(id = iconRes), - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - Spacer(modifier = Modifier.width(16.dp)) - } - Column(modifier = Modifier.weight(1f)) { - Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) + PreferenceItem( + modifier = modifier.clickable( + enabled = summary is ResourceState.Success, + onClick = onClick + ), + title = title, + iconRes = iconRes, + content = { when (summary) { ResourceState.Loading -> Box( modifier = Modifier @@ -67,16 +52,14 @@ fun ClickablePreferenceItem( style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) - - else -> Unit } - } - } + }, + ) } @Preview(showBackground = true) @Composable -internal fun SeekBarClickablePreferenceItemPreview( +internal fun ClickablePreferenceItemPreview( @PreviewParameter(ClickablePreferenceItemPreviewProvider::class) preview: ResourceState, ) { PhyphoxTheme { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt similarity index 95% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt index e4c02cef..624ae8ad 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/ClickablePreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState From c1df17ad04097f4506e81de13e80d4acf5d9968a Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:00:21 +0100 Subject: [PATCH 035/111] update ClickablePreferenceItem.kt --- .../ClickablePreferenceItem.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt index 6db6e10b..c923fa19 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -5,20 +5,18 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel -import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable @@ -33,7 +31,7 @@ fun ClickablePreferenceItem( PreferenceItem( modifier = modifier.clickable( enabled = summary is ResourceState.Success, - onClick = onClick + onClick = onClick, ), title = title, iconRes = iconRes, @@ -47,10 +45,8 @@ fun ClickablePreferenceItem( .skeleton(), ) - is ResourceState.Success -> Text( - text = summary.data.resolve(), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, + is ResourceState.Success -> PreferenceSummaryItem( + text = summary.data, ) } }, From 9e8a22362d41f149be88f48a2d9cb3aebe54b077 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:00:36 +0100 Subject: [PATCH 036/111] update default radius for skeleton extension --- .../main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt index 1374a0ca..225de182 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/ModifierExtensions.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp /** @@ -23,7 +24,8 @@ import androidx.compose.ui.unit.dp */ fun Modifier.skeleton( show: Boolean = true, - shape: Shape = RoundedCornerShape(4.dp), + radius: Dp = 16.dp, + shape: Shape = RoundedCornerShape(radius), ): Modifier = if (show) { composed { val transition = rememberInfiniteTransition(label = "skeleton") From d75cb190973890c9259b830a960b800f11578265 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:00:48 +0100 Subject: [PATCH 037/111] add new summary item --- .../PreferenceSummaryItem.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt new file mode 100644 index 00000000..4908a7a4 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt @@ -0,0 +1,35 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem + +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + + +@Composable +fun PreferenceSummaryItem( + modifier: Modifier = Modifier, + text: StringUIModel, +) { + Text( + modifier = modifier, + text = text.resolve(), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) +} + +@PreviewLightDark +@Composable +internal fun PreferenceSummaryItemPreview() { + PhyphoxTheme { + PreferenceSummaryItem( + text = LoremIpsumStringUIModel(4), + ) + } +} From 88f34e4a7f095734dea89d5540b5fd2db46138cd Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:00:59 +0100 Subject: [PATCH 038/111] update seekbar preferenceitem --- .../SeekBarPreferenceItem.kt | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 40a7dff5..1d120b93 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -1,57 +1,58 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel -import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SeekBarPreferenceItem( title: StringUIModel, + iconRes: Int? = null, + summary: StringUIModel? = null, seekBarConfig: ResourceState, onValueChange: (Float) -> Unit, ) { - Column( - modifier = Modifier - .fillMaxWidth(), - ) { - Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) + PreferenceItem( + title = title, + iconRes = iconRes, + content = { + summary?.let { + PreferenceSummaryItem(text = summary) + } + when (seekBarConfig) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 4.dp) + .fillMaxWidth() + .height(16.dp) + .skeleton(), + ) - when (seekBarConfig) { - ResourceState.Loading -> Box( - modifier = Modifier - .padding(top = 16.dp) - .fillMaxWidth() - .height(16.dp) - .skeleton(), - ) - - is ResourceState.Success -> Slider( - value = seekBarConfig.data.currentSize, - onValueChange = onValueChange, - valueRange = seekBarConfig.data.range, - steps = (seekBarConfig.data.range.endInclusive - seekBarConfig.data.range.start).toInt() - 1, - ) - } - - } + is ResourceState.Success -> Slider( + value = seekBarConfig.data.currentSize, + onValueChange = onValueChange, + valueRange = seekBarConfig.data.range, + steps = (seekBarConfig.data.range.endInclusive - seekBarConfig.data.range.start).toInt() - 1, + ) + } + }, + ) } @Preview(showBackground = true) @@ -63,6 +64,7 @@ internal fun SeekBarPreferenceItemPreview( Surface { SeekBarPreferenceItem( title = LoremIpsumStringUIModel(2), + summary = LoremIpsumStringUIModel(12), seekBarConfig = graphSize, onValueChange = {}, ) From 21cac9b3259c4977e2962207f2ca7e0544fc3761 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:01:26 +0100 Subject: [PATCH 039/111] update Switch Preference Item --- .../SwitchPreferenceItem.kt | 76 +++++++------------ 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index b6dd722b..eb4a3049 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -3,88 +3,66 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpref import android.content.res.Configuration import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Switch -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel -import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SwitchPreferenceItem( + modifier: Modifier = Modifier, title: StringUIModel, summary: StringUIModel? = null, iconRes: Int? = null, checked: ResourceState, onCheckedChange: (Boolean) -> Unit, ) { - Row( - modifier = Modifier - .fillMaxWidth() + + PreferenceItem( + modifier = modifier .clickable(checked is ResourceState.Success) { onCheckedChange(checked.isChecked()) }, - verticalAlignment = Alignment.CenterVertically, - ) { - iconRes?.let { - Icon( - painter = painterResource(id = iconRes), - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) - Spacer(modifier = Modifier.width(16.dp)) - } - Column(modifier = Modifier.weight(1f)) { - Text(text = title.resolve(), style = MaterialTheme.typography.bodyLarge) + title = title, + iconRes = iconRes, + content = { summary?.let { - Text( - text = summary.resolve(), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + PreferenceSummaryItem(text = summary) } - } - Spacer(Modifier.size(4.dp)) - when (checked) { - ResourceState.Loading -> Box( - modifier = Modifier - .padding(top = 16.dp) - .width(48.dp) - .height(32.dp) - .skeleton(), - ) - - is ResourceState.Success -> - Switch( - checked = checked.isChecked(), - onCheckedChange = onCheckedChange, + }, + trailingContent = { + when (checked) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 16.dp) + .width(48.dp) + .height(32.dp) + .skeleton(), ) - } - } + is ResourceState.Success -> + Switch( + checked = checked.isChecked(), + onCheckedChange = onCheckedChange, + ) + } + }, + ) } @Preview( From 83380b37499c3e906beac41b1d985a63938efcff Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:02:07 +0100 Subject: [PATCH 040/111] update settings contnet to support new parameters in the composable --- .../compose/settingscontent/SettingsContent.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index ba25ed49..a5f0506c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.ClickablePreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState @@ -55,7 +55,7 @@ fun SettingsContent( ) } item { - PreferenceItem( + ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsLanguage), summary = currentLanguage, iconRes = R.drawable.setting_language, @@ -63,7 +63,7 @@ fun SettingsContent( ) } item { - PreferenceItem( + ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsTranslation), summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), iconRes = R.drawable.setting_translate, @@ -83,12 +83,14 @@ fun SettingsContent( item { SeekBarPreferenceItem( title = ResourceStringUIModel(R.string.settingGraphSize), + summary = ResourceStringUIModel(R.string.settingGraphSizeSubTitle), + iconRes = R.drawable.ic_line_width, seekBarConfig = seekbarConfig, onValueChange = onGraphSizeChanged, ) } item { - PreferenceItem( + ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settings_theme_title), summary = uiMode, iconRes = R.drawable.ic_dark_mode, @@ -104,7 +106,7 @@ fun SettingsContent( } item { //replace this with SingleChoiceSegmentedButtonRow - PreferenceItem( + ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, iconRes = R.drawable.setting_http, From 023e59cb3c0be5817eb201aa6377f00291a23590 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:18:12 +0100 Subject: [PATCH 041/111] define segmented button preference item --- .../SegmentedButtonPreferenceItem.kt | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt new file mode 100644 index 00000000..956bf76f --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -0,0 +1,102 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.SegmentedButton +import androidx.compose.material3.SegmentedButtonDefaults +import androidx.compose.material3.SingleChoiceSegmentedButtonRow +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode +import de.rwth_aachen.phyphox.ui.skeleton +import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme + +@Composable +fun SegmentedButtonPreferenceItem( + modifier: Modifier = Modifier, + title: StringUIModel, + summary: StringUIModel? = null, + iconRes: Int? = null, + options: ResourceState>, + onOptionSelected: (UiMode) -> Unit, +) { + PreferenceItem( + modifier = modifier, + title = title, + iconRes = iconRes, + content = { + summary?.let { + PreferenceSummaryItem(text = summary) + } + when (options) { + ResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 4.dp) + .fillMaxWidth() + .height(16.dp) + .skeleton(), + ) + + is ResourceState.Success> -> SingleChoiceSegmentedButtonRow( + modifier = Modifier.fillMaxWidth(), + ) { + options.data.forEachIndexed { index, option -> + SegmentedButton( + shape = SegmentedButtonDefaults.itemShape( + index = index, + count = options.data.size, + ), + onClick = { + onOptionSelected(option) + }, + selected = option.isSelected, + label = { Text(option.text.resolve()) }, + ) + } + } + } + + }, + ) +} + +@PreviewLightDark +@Composable +internal fun SegmentedButtonPreferenceItemPreview() { + PhyphoxTheme { + SegmentedButtonPreferenceItem( + title = LoremIpsumStringUIModel(4), + summary = LoremIpsumStringUIModel(12), + iconRes = R.drawable.ic_dark_mode, + options = ResourceState.Success( + listOf( + UiMode( + text = LoremIpsumStringUIModel(2), + isSelected = true, + ), + UiMode( + text = LoremIpsumStringUIModel(1), + isSelected = false, + ), + UiMode( + text = LoremIpsumStringUIModel(1), + isSelected = false, + ), + ), + ), + onOptionSelected = {}, + ) + } +} From b014c60e4f91709383e5a4a119ef0bc124774324 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:27:48 +0100 Subject: [PATCH 042/111] remove unnused import --- .../presentation/compose/preferenceitem/PreferenceItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt index dbcac3d6..d5c8a221 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt @@ -23,7 +23,6 @@ import androidx.compose.ui.tooling.preview.datasource.LoremIpsum import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve From 71b31d9cf935f372c844185c143103f58fa9ed79 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:28:09 +0100 Subject: [PATCH 043/111] add modifier parameter --- .../compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 1d120b93..554add51 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -22,6 +22,7 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SeekBarPreferenceItem( + modifier: Modifier = Modifier, title: StringUIModel, iconRes: Int? = null, summary: StringUIModel? = null, @@ -29,6 +30,7 @@ fun SeekBarPreferenceItem( onValueChange: (Float) -> Unit, ) { PreferenceItem( + modifier = modifier, title = title, iconRes = iconRes, content = { From d71d6aa0cf141d0cb46db9000f8f0f9cbfac0646 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:28:28 +0100 Subject: [PATCH 044/111] update callback event --- .../phyphox/features/settings/presentation/SettingsActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 1bc03f14..0bdde7bd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -32,7 +32,7 @@ class SettingsActivity : ComponentActivity() { onAppLanguageClicked = {}, onLearnMoreAboutTranslationClicked = {}, onGraphSizeChanged = {}, - onUiModeClicked = {}, + onOptionSelected = {}, onAccessPortClicked = {}, onProximityLockChanged = {}, ) From 7b364082af908acca16966c5e402dec072bf77e3 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:28:38 +0100 Subject: [PATCH 045/111] update callback event --- .../features/settings/presentation/compose/SettingsRoot.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 12d9d41e..feb2a930 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent.SettingsContent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @OptIn(ExperimentalMaterial3Api::class) @@ -27,7 +28,7 @@ fun SettingsRoot( onAppLanguageClicked: () -> Unit, onLearnMoreAboutTranslationClicked: () -> Unit, onGraphSizeChanged: (Float) -> Unit, - onUiModeClicked: () -> Unit, + onOptionSelected: (UiMode) -> Unit, onAccessPortClicked: () -> Unit, onProximityLockChanged: (Boolean) -> Unit, ) { @@ -57,7 +58,7 @@ fun SettingsRoot( onAppLanguageClicked = onAppLanguageClicked, onLearnMoreAboutTranslationClicked = onLearnMoreAboutTranslationClicked, onGraphSizeChanged = onGraphSizeChanged, - onUiModeClicked = onUiModeClicked, + onOptionSelected = onOptionSelected, onAccessPortClicked = onAccessPortClicked, onProximityLockChanged = onProximityLockChanged, ) @@ -73,7 +74,7 @@ internal fun SettingsRootPreview() { onAppLanguageClicked = {}, onLearnMoreAboutTranslationClicked = {}, onGraphSizeChanged = {}, - onUiModeClicked = {}, + onOptionSelected = {}, onAccessPortClicked = {}, onProximityLockChanged = {}, ) From 619626a43fcafacc4664cbdad7c61d5f56c34234 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:29:29 +0100 Subject: [PATCH 046/111] define new uimode class and update the type of uimodel --- .../presentation/viewmodel/SettingsUiState.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 6ee91d9e..10912afb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -5,12 +5,12 @@ import de.rwth_aachen.phyphox.ui.string.StringUIModel data class SettingsUiState( val currentLanguage: ResourceState = ResourceState.Loading, val graphSize: ResourceState = ResourceState.Loading, - val uiMode: ResourceState = ResourceState.Loading, + val uiMode: ResourceState> = ResourceState.Loading, // val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, -) + ) sealed interface ResourceState { data object Loading : ResourceState @@ -29,8 +29,13 @@ data class SeekBarConfig( currentSize: Float, minSize: Float, maxSize: Float, - ): this( + ) : this( currentSize = currentSize, range = minSize..maxSize, ) } + +data class UiMode( + val text: StringUIModel, + val isSelected: Boolean, +) From 394117676777bbd0362d37ca23de526ce5cfb0f3 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 20:29:43 +0100 Subject: [PATCH 047/111] update SettingsContent.kt --- .../settingscontent/SettingsContent.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index a5f0506c..0a78195e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -14,12 +14,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.ClickablePreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -30,13 +32,13 @@ fun SettingsContent( modifier: Modifier = Modifier, currentLanguage: ResourceState, seekbarConfig: ResourceState, - uiMode: ResourceState, + uiMode: ResourceState>, accessPort: ResourceState, proximityLockEnabled: ResourceState, onAppLanguageClicked: () -> Unit, onLearnMoreAboutTranslationClicked: () -> Unit, onGraphSizeChanged: (Float) -> Unit, - onUiModeClicked: () -> Unit, + onOptionSelected: (UiMode) -> Unit, onAccessPortClicked: () -> Unit, onProximityLockChanged: (Boolean) -> Unit, ) { @@ -90,11 +92,11 @@ fun SettingsContent( ) } item { - ClickablePreferenceItem( + SegmentedButtonPreferenceItem( title = ResourceStringUIModel(resId = R.string.settings_theme_title), - summary = uiMode, + options = uiMode, iconRes = R.drawable.ic_dark_mode, - onClick = onUiModeClicked + onOptionSelected = onOptionSelected, ) } item { @@ -102,10 +104,11 @@ fun SettingsContent( } // Advanced Category item { - PreferenceCategoryHeader(title = ResourceStringUIModel(resId = R.string.settingsHeadAdvanced)) + PreferenceCategoryHeader( + title = ResourceStringUIModel(resId = R.string.settingsHeadAdvanced), + ) } item { - //replace this with SingleChoiceSegmentedButtonRow ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, @@ -148,7 +151,7 @@ fun SettingsContentPreview() { onAppLanguageClicked = {}, onLearnMoreAboutTranslationClicked = {}, onGraphSizeChanged = {}, - onUiModeClicked = {}, + onOptionSelected = {}, onAccessPortClicked = {}, onProximityLockChanged = {}, ) From 65883457b1615fb5a222db503f18f031bd3c2ac8 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:01:04 +0100 Subject: [PATCH 048/111] preparing viewmodel --- .../settings/domain/model/AppUiMode.kt | 13 ++++ .../presentation/compose/SettingsRoot.kt | 6 +- .../SegmentedButtonPreferenceItem.kt | 14 ++--- .../settingscontent/SettingsContent.kt | 10 +-- .../presentation/viewmodel/SettingsUiState.kt | 6 +- .../viewmodel/SettingsViewModel.kt | 61 +++++++++++++++++-- .../presentation/viewmodel/UiBuilder.kt | 41 +++++++++++++ 7 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt new file mode 100644 index 00000000..74a53bc7 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt @@ -0,0 +1,13 @@ +package de.rwth_aachen.phyphox.features.settings.domain.model + +enum class AppUiMode(val identifier: String) { + SYSTEM("system"), + LIGHT("light"), + DARK("dark"); + + companion object { + fun fromString(identifier: String): AppUiMode? { + return entries.find { it.identifier == identifier } + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index feb2a930..86cf871c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent.SettingsContent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @OptIn(ExperimentalMaterial3Api::class) @@ -28,7 +28,7 @@ fun SettingsRoot( onAppLanguageClicked: () -> Unit, onLearnMoreAboutTranslationClicked: () -> Unit, onGraphSizeChanged: (Float) -> Unit, - onOptionSelected: (UiMode) -> Unit, + onOptionSelected: (UiModeUiModel) -> Unit, onAccessPortClicked: () -> Unit, onProximityLockChanged: (Boolean) -> Unit, ) { @@ -52,7 +52,7 @@ fun SettingsRoot( modifier = modifier.padding(innerPadding), currentLanguage = uiState.currentLanguage, seekbarConfig = uiState.graphSize, - uiMode = uiState.uiMode, + uiModeUiModel = uiState.uiModeUiModel, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, onAppLanguageClicked = onAppLanguageClicked, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index 956bf76f..d125b8ca 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -16,7 +16,7 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel @@ -29,8 +29,8 @@ fun SegmentedButtonPreferenceItem( title: StringUIModel, summary: StringUIModel? = null, iconRes: Int? = null, - options: ResourceState>, - onOptionSelected: (UiMode) -> Unit, + options: ResourceState>, + onOptionSelected: (UiModeUiModel) -> Unit, ) { PreferenceItem( modifier = modifier, @@ -49,7 +49,7 @@ fun SegmentedButtonPreferenceItem( .skeleton(), ) - is ResourceState.Success> -> SingleChoiceSegmentedButtonRow( + is ResourceState.Success> -> SingleChoiceSegmentedButtonRow( modifier = Modifier.fillMaxWidth(), ) { options.data.forEachIndexed { index, option -> @@ -82,15 +82,15 @@ internal fun SegmentedButtonPreferenceItemPreview() { iconRes = R.drawable.ic_dark_mode, options = ResourceState.Success( listOf( - UiMode( + UiModeUiModel( text = LoremIpsumStringUIModel(2), isSelected = true, ), - UiMode( + UiModeUiModel( text = LoremIpsumStringUIModel(1), isSelected = false, ), - UiMode( + UiModeUiModel( text = LoremIpsumStringUIModel(1), isSelected = false, ), diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 0a78195e..03e1f7fe 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -21,7 +21,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbu import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiMode +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -32,13 +32,13 @@ fun SettingsContent( modifier: Modifier = Modifier, currentLanguage: ResourceState, seekbarConfig: ResourceState, - uiMode: ResourceState>, + uiModeUiModel: ResourceState>, accessPort: ResourceState, proximityLockEnabled: ResourceState, onAppLanguageClicked: () -> Unit, onLearnMoreAboutTranslationClicked: () -> Unit, onGraphSizeChanged: (Float) -> Unit, - onOptionSelected: (UiMode) -> Unit, + onOptionSelected: (UiModeUiModel) -> Unit, onAccessPortClicked: () -> Unit, onProximityLockChanged: (Boolean) -> Unit, ) { @@ -94,7 +94,7 @@ fun SettingsContent( item { SegmentedButtonPreferenceItem( title = ResourceStringUIModel(resId = R.string.settings_theme_title), - options = uiMode, + options = uiModeUiModel, iconRes = R.drawable.ic_dark_mode, onOptionSelected = onOptionSelected, ) @@ -145,7 +145,7 @@ fun SettingsContentPreview() { SettingsContent( currentLanguage = ResourceState.Loading, seekbarConfig = ResourceState.Loading, - uiMode = ResourceState.Loading, + uiModeUiModel = ResourceState.Loading, accessPort = ResourceState.Loading, proximityLockEnabled = ResourceState.Loading, onAppLanguageClicked = {}, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 10912afb..7fc77580 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -1,11 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.ui.string.StringUIModel data class SettingsUiState( val currentLanguage: ResourceState = ResourceState.Loading, val graphSize: ResourceState = ResourceState.Loading, - val uiMode: ResourceState> = ResourceState.Loading, + val uiModeUiModel: ResourceState> = ResourceState.Loading, // val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, @@ -35,7 +36,8 @@ data class SeekBarConfig( ) } -data class UiMode( +data class UiModeUiModel( + val appUiMode: AppUiMode, val text: StringUIModel, val isSelected: Boolean, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 9edf19bd..1812cecd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -2,13 +2,66 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.ui.string.StringUIModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import javax.inject.Inject @HiltViewModel -class SettingsViewModel @Inject constructor() : ViewModel() { +internal class SettingsViewModel @Inject constructor( + private val uiBuilder: UiBuilder, +) : ViewModel() { - private val _uiState = MutableStateFlow(SettingsUiState()) - val uiState = _uiState.asStateFlow() + private val currentLanguageFlow = MutableStateFlow>(ResourceState.Loading) + private val supportedLanguagesFlow = MutableStateFlow>>(ResourceState.Loading) + private val currentGraphSizeFlow = MutableStateFlow>(ResourceState.Loading) + private val graphSizeRangeFlow = + MutableStateFlow>>(ResourceState.Loading) + private val currentUiModeUiModelFlow = MutableStateFlow>(ResourceState.Loading) + private val supportedUiModesFlowUiModel = MutableStateFlow>>(ResourceState.Loading) + private val currentAccessPortFlow = MutableStateFlow>(ResourceState.Loading) + private val proximityLockEnabledFlow = MutableStateFlow>(ResourceState.Loading) + + private val graphSizeFlow = combine(currentGraphSizeFlow, graphSizeRangeFlow) { graphSize, range -> + if (graphSize is ResourceState.Success && range is ResourceState.Success) { + ResourceState.Success( + data = uiBuilder.buildSeekBarConfig( + currentSize = graphSize.data, + range = range.data, + ), + ) + } else { + ResourceState.Loading + } + } + + private val uiModeFlow = combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> + if (current is ResourceState.Success && modes is ResourceState.Success) { + ResourceState.Success( + uiBuilder.buildUiModeUiModels( + currentUiModeUiMode = current.data, + supportedModes = modes.data, + ), + ) + } else { + ResourceState.Loading + } + } + + val uiState = combine( + currentLanguageFlow, + graphSizeFlow, + uiModeFlow, + currentAccessPortFlow, + proximityLockEnabledFlow, + ) { currentLanguage, graphSize, uiMode, accessPort, proximityLockEnabled -> + SettingsUiState( + currentLanguage = currentLanguage, + graphSize = graphSize, + uiModeUiModel = uiMode, + accessPort = accessPort, + proximityLockEnabled = proximityLockEnabled, + ) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt new file mode 100644 index 00000000..d6f8b066 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -0,0 +1,41 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import javax.inject.Inject + +internal class UiBuilder @Inject constructor() { + fun buildSeekBarConfig( + currentSize: Float, + range: ClosedFloatingPointRange, + ): SeekBarConfig { + return SeekBarConfig( + currentSize = currentSize, + minSize = range.start, + maxSize = range.endInclusive, + ) + } + + fun buildUiModeUiModels( + currentUiModeUiMode: AppUiMode, + supportedModes: List, + ): List { + return supportedModes.map { mode -> + UiModeUiModel( + appUiMode = mode, + text = getSupportedUiModeText(mode), + isSelected = mode == currentUiModeUiMode, + ) + } + } + + private fun getSupportedUiModeText(appUiMode: AppUiMode): StringUIModel { + return when (appUiMode) { + AppUiMode.SYSTEM -> ResourceStringUIModel(R.string.settings_mode_dark_system) + AppUiMode.LIGHT -> ResourceStringUIModel(R.string.settings_mode_no_dark) + AppUiMode.DARK -> ResourceStringUIModel(R.string.settings_mode_dark) + } + } +} From c17b0f97b2281b8f3d5f90fcaad7e6c81b5232ae Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:10:58 +0100 Subject: [PATCH 049/111] dummy viewmodel --- .../settings/domain/model/AppUiMode.kt | 6 ++- .../SegmentedButtonPreferenceItem.kt | 5 +++ .../viewmodel/SettingsViewModel.kt | 44 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt index 74a53bc7..7d83b6ff 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt @@ -1,9 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.domain.model enum class AppUiMode(val identifier: String) { - SYSTEM("system"), + DARK("dark"), LIGHT("light"), - DARK("dark"); + SYSTEM("system"); + + companion object { fun fromString(identifier: String): AppUiMode? { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index d125b8ca..2da9fbd0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -13,9 +13,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel @@ -83,14 +85,17 @@ internal fun SegmentedButtonPreferenceItemPreview() { options = ResourceState.Success( listOf( UiModeUiModel( + appUiMode = AppUiMode.DARK, text = LoremIpsumStringUIModel(2), isSelected = true, ), UiModeUiModel( + appUiMode = AppUiMode.SYSTEM, text = LoremIpsumStringUIModel(1), isSelected = false, ), UiModeUiModel( + appUiMode = AppUiMode.LIGHT, text = LoremIpsumStringUIModel(1), isSelected = false, ), diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 1812cecd..f5fe072b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -1,11 +1,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.TextStringUIModel +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -63,5 +69,43 @@ internal class SettingsViewModel @Inject constructor( accessPort = accessPort, proximityLockEnabled = proximityLockEnabled, ) + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = SettingsUiState(), + ) + + init { + loadDummyData() + } + + private fun loadDummyData() = viewModelScope.launch { + delay(800) + currentLanguageFlow.value = ResourceState.Success(TextStringUIModel("English")) + delay(1200) + currentGraphSizeFlow.value = ResourceState.Success( + data = 1.0f, + ) + delay(200) + graphSizeRangeFlow.value = ResourceState.Success( + data = 0.0f..3.0f, + ) + delay(800) + currentUiModeUiModelFlow.value = ResourceState.Success( + data = AppUiMode.DARK, + ) + supportedUiModesFlowUiModel.value = ResourceState.Success( + AppUiMode.entries, + ) + delay(600) + currentAccessPortFlow.value = ResourceState.Success( + data = TextStringUIModel("8080"), + ) + delay(2000) + proximityLockEnabledFlow.value = ResourceState.Success( + data = true, + ) + + } } From 9186ab8a263136595ef4ffea9a3b0549ace71188 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:11:26 +0100 Subject: [PATCH 050/111] dummy viewmodel --- .../phyphox/features/settings/domain/model/AppUiMode.kt | 1 + .../SegmentedButtonPreferenceItem.kt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt index 7d83b6ff..9a2ef654 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppUiMode.kt @@ -8,6 +8,7 @@ enum class AppUiMode(val identifier: String) { companion object { + @Suppress("unused") fun fromString(identifier: String): AppUiMode? { return entries.find { it.identifier == identifier } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index 2da9fbd0..810ba8cc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -17,7 +17,6 @@ import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel From 96efe1f8caf86dbdd8f9650fa4227dcd3713923b Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:50:52 +0100 Subject: [PATCH 051/111] add logo --- .../drawable-night-xxxhdpi/settings_logo.png | Bin 0 -> 48800 bytes .../res/drawable-xxxhdpi/settings_logo.png | Bin 0 -> 856813 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/drawable-night-xxxhdpi/settings_logo.png create mode 100644 app/src/main/res/drawable-xxxhdpi/settings_logo.png diff --git a/app/src/main/res/drawable-night-xxxhdpi/settings_logo.png b/app/src/main/res/drawable-night-xxxhdpi/settings_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..99ede5ae3bacd073cf279abdb84ee0998e85d580 GIT binary patch literal 48800 zcmeFYbyQVdyFR>VgiT3@l%Sx*raJ|syHQ}%n{GCZq_il4q|yo^64D_cARw)zw1jkr z)Hiva=Y7w6#_u=AH_jN}_uoNq?7h~UbIo)2h4^>ix;s+W(9=*&Rn*eOi3?%nVqwjNa&iUtAxK;b<%+O$u=ZrMu(m}y zOE7OYv@kOwtt6NY1k~Vat`Ds3kcxip);fObx|V(pmLgWnQj&P$C{ZwgleH&;5#{9Q z>>-MhVE$`dQSkZtGB-2hUrjt6B$y4=G#MYbxLY&wbMbS*IptAEZ(e3eJVtSMD;rU5 zIfZ|Q0N*5-?L0kQMY*|se0;ck__$o$ZMk_wL`1mZyxhFJoS+4#hp)3I0>$akIT3IR9!?Q!eqk#CODjPUOX2?*O3B&76X9%WeLWNyoC^uYvEhRYi||_8aPsqr z@Nx3n@bGhr*jNa03S05=3tI9D@K}ln{>Km+?nt1O2*>}N)%8$TU??FQD`6`^D;`cu z8ygWY8$l6H5ng^PPHO=XxRtPlhy|aG&0j-VS&AySxH}=haw44&w$|LP&bEKuxE8pm zjHZ$VGancHzpiLHB0OzC2MK0Xq_Y?5zi#Lvovd{{5!ce>5fT*Q1*!?>6XFx*6XN}^ zM*7z79zYZS8k6hqce!2^QSd52tcYtZ1>OH@58g!dfx9)r)5Tra#l=yA`FcYCzM}^A zkrl!dA&2m^mSEcUwep=@vq$=im?29_8tgtYpcKJ3C8;SmZcrS z+147Ejeqp?U)PcU7ag$V;k6PH1Ug`2!z0AWFDw8w$jVBH(@Mw^A;>R+un-pE`|qQB zxY&65Al$8GY=H>>784^_GA=0EYhGhy0J^`@iD)Uvd49 zB=A3K{J+}uzvB8IN#K9f_(TlDHfWC_Cr~;iEfTy z@CDOWMok8S$`kR=5m?|mqoty@8U*=)h9Th)^aorDS%n}kI0UVkLy+hz2%>b&tk)6; zH?S>K9?C&i*Z7Px#DE<6U4H2AYv#) z0oU5f+L}Bz*A5M$C@q!EFZp|pjKr<}>c|;aVGiZzWb(LyYTt(Wo%+6h{VG5cPX|#w zU1j~JUFh*6jG9lMBIL9j+o={SaZM{bEV}t=;$fNPfw*!9H~)I$zec+;Gg731Bd>Lk zwVBGVwjau97_V^K;gc~5^zp_}FUgR~-!BPS4NcZDqI@>SNnY_IweLd#oNckh7RYo# zY8W<`m>4Zqm`o;d69iF}mOk&66-1whQBzabH_uK^D$~SEul{bXNI#r5XoVyB+df+T zHsp@0U|UDwUAZ)<<_utP1fVwfNg8j7UmT3IPPw!+>5cHm7yj;Vv0;zlO;f!yv*gbD z&~qX`!UfDdQeuC(FU&UE7h6Yn`)+K2B*V8?JYnCGiE$shwqtUi+dazraNN8528%0@ zPj2ge+sFAFYUcngXJW@kw@>5=HgWwESt?=W zkxv9FNd_c{lbuOx!AFkBsN=Pbon!3zoUE5c!Na=^_q%?zemX@2T-7RwcdkV;Y}v`S zPqgDhZ@@Z|$(NHu&3jfkYf>SBKH?k%R2;03r|c@ESgxKWF{C_}Oio4DPv$YCnWylLlS zzowZFxpVx_HVCd&9V{tETVkQVeqzPLxuM(Cc8l{gCw-x?^9cd*nU>IYv-fj5cY9$c z1|EDo(C2~6AGMcFqBxIq#*bPmd-tNsq9~z{Y%vI<6k5_0EQ)4PJ%?}KzHxl*>)Sl{ zP_*K02a1Chf=yNyg2W7|Bx5Wg`<#R?HSITuny^kyd#_)@L!Y=G{oAa(w3M?R(H{Ak z)B~U8?KHvM86DJ{f0y)c<@9Xr5NTbJbSiJVZ?V7YA##D5r{3$a{jIsstt?62WVj()R{A*-`QSMIlm5UW+UI89tMh ztx<}I3R4aD@e`f|i#S^LnBqdVwehDUw+o}~kA;gh_yR&;oR<8oBjrHf&5UePd_t_C!jr;1!Sy}Pw+2Tq{rKQ3* zqr-u23G*mw>gv5@Rk{hjuu;UapPrsl(!?udFV@nfSU1si`6AQ%-yqYKth`rGq=#Ex z1Y_HpOk@+cVMA<~A?VPnc%Bq%qf2zADjq&Q50xn&9|L}V{*$irP4}0cUVrOnJ9&0z z$nIDHc|5$)D=7vIHn)(G!^Ql0c8#FpR!ibrJCTRTtF)BJM2g0cii!%hj6MbYP-!i) z4!iVD@h8Nw#0$vuWrcVeYkPZpn)&0i%?6+Cnd!knVz72j(H$0>6&z94MhR+}!b5_= zm*+0j@d`xpTDPntk>SKyg%8vyI{9UvS{r%a?5(&>E^mYmm04R`H$C#-e@L*oxw%(s zP{!hG`?sRP#L#)M_zPMOD$ajg6$>{tFRsui>>%>6V!rWO;oWRBwS2%}aNe*J9y{VH zhv;`wY5#pS8RIBOJIqg@^jr=RmsDI_)%5A5t&t!plP(0w^Q%$riEl3n_eT=*wxw<0 zDa3YI5Mqb5j+y#zCYsbZmrD8WS=Clo3+n6Wkm5k7rI!W@%;9$7�N?2sBM~2qED} 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⪚RgxS{(0yN_I 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 ac=|U^`ow3ioFoLM9shx;f1LdB3;#cF&f*~e literal 0 HcmV?d00001 From 6963d594b5ae0e5e7cb434b41bff7450f96de10c Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:51:46 +0100 Subject: [PATCH 052/111] add logo to settings page --- .../settingscontent/SettingsContent.kt | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 03e1f7fe..af3ef3ea 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -1,9 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent import android.content.res.Configuration +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider @@ -11,6 +16,8 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R @@ -49,8 +56,21 @@ fun SettingsContent( horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(16.dp), ) { - //TODO: Once the experiment list has been migrated to compose - // move the logo from top bar in experiment list to here + item { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + Image( + modifier = Modifier + .width(172.dp), + contentScale = ContentScale.Fit, + painter = painterResource(R.drawable.settings_logo), + contentDescription = null, + ) + } + } + item { HorizontalDivider() } item { PreferenceCategoryHeader( title = ResourceStringUIModel(resId = R.string.settingsHeadLanguage), @@ -73,9 +93,7 @@ fun SettingsContent( ) } - item { - HorizontalDivider() - } + item { HorizontalDivider() } // Graph View Category item { PreferenceCategoryHeader( From 353e79ae40a028494417d56578e234a228e2ced2 Mon Sep 17 00:00:00 2001 From: owl Date: Thu, 1 Jan 2026 21:52:03 +0100 Subject: [PATCH 053/111] add logo to settings page --- .../presentation/compose/settingscontent/SettingsContent.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index af3ef3ea..178f902c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ExperimentalMaterial3Api From 7a35992165a2c39b48e083f69c126928de928b69 Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 2 Jan 2026 15:42:40 +0100 Subject: [PATCH 054/111] add interaction handlers in viewmodel --- .../settings/presentation/SettingsActivity.kt | 12 ++++++------ .../presentation/viewmodel/SettingsViewModel.kt | 9 +++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 0bdde7bd..45c2b21f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -29,12 +29,12 @@ class SettingsActivity : ComponentActivity() { SettingsRoot( uiState = uiState, onBackClick = { onBackPressedDispatcher?.onBackPressed() }, - onAppLanguageClicked = {}, - onLearnMoreAboutTranslationClicked = {}, - onGraphSizeChanged = {}, - onOptionSelected = {}, - onAccessPortClicked = {}, - onProximityLockChanged = {}, + onAppLanguageClicked = viewModel::onAppLanguageClicked, + onLearnMoreAboutTranslationClicked = viewModel::onLearnMoreAboutTranslationClicked, + onGraphSizeChanged = viewModel::onGraphSizeChanged, + onOptionSelected = viewModel::onOptionSelected, + onAccessPortClicked = viewModel::onAccessPortClicked, + onProximityLockChanged = viewModel::onProximityLockChanged, ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index f5fe072b..76e59b55 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -105,7 +105,12 @@ internal class SettingsViewModel @Inject constructor( proximityLockEnabledFlow.value = ResourceState.Success( data = true, ) - - } + + fun onAppLanguageClicked() {} + fun onLearnMoreAboutTranslationClicked() {} + fun onGraphSizeChanged(size: Float) {} + fun onOptionSelected(appUiMode: UiModeUiModel) {} + fun onAccessPortClicked() {} + fun onProximityLockChanged(enabled: Boolean) {} } From bc76e6d8cf88f1a0265221daf0569d6b8e779ba9 Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 2 Jan 2026 19:16:43 +0100 Subject: [PATCH 055/111] switch to MVI --- .../settings/presentation/SettingsActivity.kt | 8 +--- .../presentation/compose/SettingsRoot.kt | 31 +++++---------- .../settingscontent/SettingsContent.kt | 39 ++++++++++--------- .../presentation/viewmodel/SettingsAction.kt | 11 ++++++ .../viewmodel/SettingsViewModel.kt | 9 ++--- 5 files changed, 46 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 45c2b21f..6a090e01 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -28,13 +28,7 @@ class SettingsActivity : ComponentActivity() { val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher SettingsRoot( uiState = uiState, - onBackClick = { onBackPressedDispatcher?.onBackPressed() }, - onAppLanguageClicked = viewModel::onAppLanguageClicked, - onLearnMoreAboutTranslationClicked = viewModel::onLearnMoreAboutTranslationClicked, - onGraphSizeChanged = viewModel::onGraphSizeChanged, - onOptionSelected = viewModel::onOptionSelected, - onAccessPortClicked = viewModel::onAccessPortClicked, - onProximityLockChanged = viewModel::onProximityLockChanged, + onActionEvent = viewModel::onActionEvent ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 86cf871c..5caa905e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent.SettingsContent +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @OptIn(ExperimentalMaterial3Api::class) @@ -24,20 +24,18 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme fun SettingsRoot( modifier: Modifier = Modifier, uiState: SettingsUiState, - onBackClick: () -> Unit = {}, - onAppLanguageClicked: () -> Unit, - onLearnMoreAboutTranslationClicked: () -> Unit, - onGraphSizeChanged: (Float) -> Unit, - onOptionSelected: (UiModeUiModel) -> Unit, - onAccessPortClicked: () -> Unit, - onProximityLockChanged: (Boolean) -> Unit, + onActionEvent: (SettingsAction) -> Unit, ) { Scaffold( topBar = { TopAppBar( title = { Text(stringResource(id = R.string.action_settings)) }, navigationIcon = { - IconButton(onClick = onBackClick) { + IconButton( + onClick = { + onActionEvent(SettingsAction.OnBackPressed) + }, + ) { Icon( imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(id = R.string.back), @@ -55,12 +53,8 @@ fun SettingsRoot( uiModeUiModel = uiState.uiModeUiModel, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, - onAppLanguageClicked = onAppLanguageClicked, - onLearnMoreAboutTranslationClicked = onLearnMoreAboutTranslationClicked, - onGraphSizeChanged = onGraphSizeChanged, - onOptionSelected = onOptionSelected, - onAccessPortClicked = onAccessPortClicked, - onProximityLockChanged = onProximityLockChanged, + onActionEvent = onActionEvent, + ) } } @@ -71,12 +65,7 @@ internal fun SettingsRootPreview() { PhyphoxTheme { SettingsRoot( uiState = SettingsUiState(), - onAppLanguageClicked = {}, - onLearnMoreAboutTranslationClicked = {}, - onGraphSizeChanged = {}, - onOptionSelected = {}, - onAccessPortClicked = {}, - onProximityLockChanged = {}, + onActionEvent = {} ) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 178f902c..7ed2d3a7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -27,6 +27,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbu import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel @@ -41,12 +42,7 @@ fun SettingsContent( uiModeUiModel: ResourceState>, accessPort: ResourceState, proximityLockEnabled: ResourceState, - onAppLanguageClicked: () -> Unit, - onLearnMoreAboutTranslationClicked: () -> Unit, - onGraphSizeChanged: (Float) -> Unit, - onOptionSelected: (UiModeUiModel) -> Unit, - onAccessPortClicked: () -> Unit, - onProximityLockChanged: (Boolean) -> Unit, + onActionEvent: (SettingsAction) -> Unit, ) { LazyColumn( modifier = modifier @@ -80,7 +76,9 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsLanguage), summary = currentLanguage, iconRes = R.drawable.setting_language, - onClick = onAppLanguageClicked, + onClick = { + onActionEvent(SettingsAction.OnAppLanguageClicked) + }, ) } item { @@ -88,7 +86,9 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsTranslation), summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), iconRes = R.drawable.setting_translate, - onClick = onLearnMoreAboutTranslationClicked, + onClick = { + onActionEvent(SettingsAction.OnLearnMoreAboutTranslationClicked) + }, ) } @@ -105,7 +105,9 @@ fun SettingsContent( summary = ResourceStringUIModel(R.string.settingGraphSizeSubTitle), iconRes = R.drawable.ic_line_width, seekBarConfig = seekbarConfig, - onValueChange = onGraphSizeChanged, + onValueChange = { + onActionEvent(SettingsAction.OnGraphSizeChanged(it)) + }, ) } item { @@ -113,7 +115,9 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settings_theme_title), options = uiModeUiModel, iconRes = R.drawable.ic_dark_mode, - onOptionSelected = onOptionSelected, + onOptionSelected = { + onActionEvent(SettingsAction.OnUiModeItemSelected(it)) + }, ) } item { @@ -130,7 +134,9 @@ fun SettingsContent( title = ResourceStringUIModel(resId = R.string.settingsPort), summary = accessPort, iconRes = R.drawable.setting_http, - onClick = onAccessPortClicked, + onClick = { + onActionEvent(SettingsAction.OnAccessPortClicked) + }, ) } item { @@ -139,7 +145,9 @@ fun SettingsContent( summary = ResourceStringUIModel(resId = R.string.settingsProximityLockDetail), iconRes = R.drawable.setting_lock, checked = proximityLockEnabled, - onCheckedChange = onProximityLockChanged, + onCheckedChange = { + onActionEvent(SettingsAction.OnProximityLockChanged(it)) + }, ) } } @@ -165,12 +173,7 @@ fun SettingsContentPreview() { uiModeUiModel = ResourceState.Loading, accessPort = ResourceState.Loading, proximityLockEnabled = ResourceState.Loading, - onAppLanguageClicked = {}, - onLearnMoreAboutTranslationClicked = {}, - onGraphSizeChanged = {}, - onOptionSelected = {}, - onAccessPortClicked = {}, - onProximityLockChanged = {}, + onActionEvent = {}, ) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt new file mode 100644 index 00000000..473e5e6a --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -0,0 +1,11 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +sealed interface SettingsAction { + data class OnUiModeItemSelected(val appUiMode: UiModeUiModel) : SettingsAction + data class OnGraphSizeChanged(val size: Float) : SettingsAction + data class OnProximityLockChanged(val enabled: Boolean) : SettingsAction + data object OnAppLanguageClicked : SettingsAction + data object OnLearnMoreAboutTranslationClicked : SettingsAction + data object OnAccessPortClicked : SettingsAction + data object OnBackPressed : SettingsAction +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 76e59b55..c34b503d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -107,10 +107,7 @@ internal class SettingsViewModel @Inject constructor( ) } - fun onAppLanguageClicked() {} - fun onLearnMoreAboutTranslationClicked() {} - fun onGraphSizeChanged(size: Float) {} - fun onOptionSelected(appUiMode: UiModeUiModel) {} - fun onAccessPortClicked() {} - fun onProximityLockChanged(enabled: Boolean) {} + fun onActionEvent(action: SettingsAction){ + + } } From 85b49b27a14b3981ce2ed4166e2155b91233decb Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 2 Jan 2026 20:00:38 +0100 Subject: [PATCH 056/111] add ui events --- .../settings/presentation/SettingsActivity.kt | 29 ++++++++++++++++-- .../presentation/viewmodel/SettingsEvent.kt | 8 +++++ .../presentation/viewmodel/SettingsUiState.kt | 13 ++++++-- .../viewmodel/SettingsViewModel.kt | 5 ++++ .../rwth_aachen/phyphox/utils/UIEventFlow.kt | 30 +++++++++++++++++++ 5 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/utils/UIEventFlow.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt index 6a090e01..2a6e4ec4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt @@ -7,12 +7,20 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalContext +import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint import de.rwth_aachen.phyphox.features.settings.presentation.compose.SettingsRoot +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import kotlinx.coroutines.launch @AndroidEntryPoint class SettingsActivity : ComponentActivity() { @@ -25,14 +33,31 @@ class SettingsActivity : ComponentActivity() { PhyphoxTheme { Surface { val uiState by viewModel.uiState.collectAsStateWithLifecycle() - val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + ObserveSettingsEvents() SettingsRoot( uiState = uiState, - onActionEvent = viewModel::onActionEvent + onActionEvent = viewModel::onActionEvent, ) } } } } + + + @Composable + private fun ObserveSettingsEvents(){ + val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + LaunchedEffect(Unit) { + viewModel.uiEvent.collect { event -> + when (event) { + SettingsEvent.NavigateBack -> onBackPressedDispatcher?.onBackPressed() + SettingsEvent.NavigateToLanguagePicker -> {} + is SettingsEvent.OpenWebpage -> {} + } + } + } + + } + } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt new file mode 100644 index 00000000..dd2250f3 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +sealed interface SettingsEvent { + data class OpenWebpage(val url: String) : SettingsEvent + data object NavigateBack : SettingsEvent + data object NavigateToLanguagePicker : SettingsEvent + data object ShowAccessPortModal : SettingsAction +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 7fc77580..b3aa5d49 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -10,8 +10,8 @@ data class SettingsUiState( // val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: ResourceState = ResourceState.Loading, val proximityLockEnabled: ResourceState = ResourceState.Loading, - - ) + val modal: SettingsModal? = null, +) sealed interface ResourceState { data object Loading : ResourceState @@ -41,3 +41,12 @@ data class UiModeUiModel( val text: StringUIModel, val isSelected: Boolean, ) + +sealed interface SettingsModal { + data class AccessPortModal( + val currentPort: StringUIModel, + val range: ClosedFloatingPointRange, + val error: StringUIModel? = null, + ) : SettingsModal + +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index c34b503d..133732c7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -6,6 +6,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.TextStringUIModel +import de.rwth_aachen.phyphox.utils.UIEventFlow +import de.rwth_aachen.phyphox.utils.asFlow import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -29,6 +31,9 @@ internal class SettingsViewModel @Inject constructor( private val currentAccessPortFlow = MutableStateFlow>(ResourceState.Loading) private val proximityLockEnabledFlow = MutableStateFlow>(ResourceState.Loading) + private val _uiEvent = UIEventFlow() + val uiEvent = _uiEvent.asFlow() + private val graphSizeFlow = combine(currentGraphSizeFlow, graphSizeRangeFlow) { graphSize, range -> if (graphSize is ResourceState.Success && range is ResourceState.Success) { ResourceState.Success( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/utils/UIEventFlow.kt b/app/src/main/java/de/rwth_aachen/phyphox/utils/UIEventFlow.kt new file mode 100644 index 00000000..41400f4f --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/utils/UIEventFlow.kt @@ -0,0 +1,30 @@ +package de.rwth_aachen.phyphox.utils + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.AbstractFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.withContext + +@OptIn(ExperimentalCoroutinesApi::class) +class UIEventFlow : AbstractFlow() { + + private val events = Channel(capacity = 32, onBufferOverflow = BufferOverflow.DROP_OLDEST) + override suspend fun collectSafely(collector: FlowCollector) { + events.receiveAsFlow().collect { + collector.emit(it) + } + } + + suspend fun emit(event: T) { + withContext(Dispatchers.Main.immediate) { + events.send(event) + } + } +} + +fun UIEventFlow.asFlow(): Flow = this From 6e7b85ccc44f752b8266c2a4d9a2662261dfe4d6 Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 2 Jan 2026 20:11:27 +0100 Subject: [PATCH 057/111] declare usecases in viewmodel --- ....kt => ObserveCurrentAccessPortUseCase.kt} | 2 +- ...e.kt => ObserveCurrentGraphSizeUseCase.kt} | 2 +- ...kt => ObserveCurrentAppLanguageUseCase.kt} | 2 +- ...veIsCurrentProximityLockEnabledUseCase.kt} | 2 +- ...ase.kt => GetSupportedAppUiModeUseCase.kt} | 2 +- .../uimode/ObserveCurrentAppUiModeUseCase.kt | 4 ++++ ...eCase.kt => SetCurrentAppUiModeUseCase.kt} | 2 +- .../viewmodel/SettingsViewModel.kt | 19 +++++++++++++++++-- 8 files changed, 27 insertions(+), 8 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/{GetCurrentAccessPortUseCase.kt => ObserveCurrentAccessPortUseCase.kt} (66%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/{GetCurrentGraphSizeUseCase.kt => ObserveCurrentGraphSizeUseCase.kt} (66%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/{GetCurrentAppLanguageUseCase.kt => ObserveCurrentAppLanguageUseCase.kt} (64%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/{IsCurrentProximityLockEnabledUseCase.kt => ObserveIsCurrentProximityLockEnabledUseCase.kt} (60%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/{GetCurrentUiModeUseCase.kt => GetSupportedAppUiModeUseCase.kt} (66%) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/{SetCurrentUiModeUseCase.kt => SetCurrentAppUiModeUseCase.kt} (67%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt similarity index 66% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt index a4d8bbf8..fa9f29f0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetCurrentAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport -class GetCurrentAccessPortUseCase { +class ObserveCurrentAccessPortUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt similarity index 66% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt index 535009f7..6f1cc54a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetCurrentGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -class GetCurrentGraphSizeUseCase { +class ObserveCurrentGraphSizeUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt similarity index 64% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt index 6c10bd8a..f4e52fad 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetCurrentAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language -class GetCurrentAppLanguageUseCase { +class ObserveCurrentAppLanguageUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt similarity index 60% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt index ba17c7fb..5df876da 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/IsCurrentProximityLockEnabledUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock -class IsCurrentProximityLockEnabledUseCase { +class ObserveIsCurrentProximityLockEnabledUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt similarity index 66% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt index d3bea886..f18b2245 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetCurrentUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class GetCurrentUiModeUseCase { +class GetSupportedAppUiModeUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt new file mode 100644 index 00000000..d8929724 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt @@ -0,0 +1,4 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode + +class ObserveCurrentAppUiModeUseCase { +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt similarity index 67% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt index 94f0465e..c4bc02f0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class SetCurrentUiModeUseCase { +class SetCurrentAppUiModeUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 133732c7..3cf6e253 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -4,6 +4,14 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.GetGraphSizeRangeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.ObserveCurrentGraphSizeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.ObserveIsCurrentProximityLockEnabledUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.TextStringUIModel import de.rwth_aachen.phyphox.utils.UIEventFlow @@ -19,10 +27,17 @@ import javax.inject.Inject @HiltViewModel internal class SettingsViewModel @Inject constructor( private val uiBuilder: UiBuilder, + private val observeCurrentAppLanguage: ObserveCurrentAppLanguageUseCase, + private val observeCurrentGraphSize: ObserveCurrentGraphSizeUseCase, + private val getGraphSizeRange: GetGraphSizeRangeUseCase, + private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, + private val getSupportedUiModes: GetSupportedAppUiModeUseCase, + private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, + private val getAccessPortRange: GetAccessPortRangeUseCase, + private val observeProximityLockEnabled: ObserveIsCurrentProximityLockEnabledUseCase, ) : ViewModel() { private val currentLanguageFlow = MutableStateFlow>(ResourceState.Loading) - private val supportedLanguagesFlow = MutableStateFlow>>(ResourceState.Loading) private val currentGraphSizeFlow = MutableStateFlow>(ResourceState.Loading) private val graphSizeRangeFlow = MutableStateFlow>>(ResourceState.Loading) @@ -112,7 +127,7 @@ internal class SettingsViewModel @Inject constructor( ) } - fun onActionEvent(action: SettingsAction){ + fun onActionEvent(action: SettingsAction) { } } From 571b4b16a2c563c3f3ebeec8e35ce6953f591f91 Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 2 Jan 2026 20:31:11 +0100 Subject: [PATCH 058/111] defining delegates --- .../presentation/viewmodel/SettingsViewModel.kt | 3 ++- .../viewmodel/delegates/AccessPortDelegate.kt | 9 +++++++++ .../viewmodel/delegates/GraphSizeDelegate.kt | 10 ++++++++++ .../viewmodel/delegates/LanguageSettingsDelegate.kt | 13 +++++++++++++ .../viewmodel/delegates/ProximityLockDelegate.kt | 8 ++++++++ .../viewmodel/delegates/UiModeDelegate.kt | 10 ++++++++++ 6 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 3cf6e253..b9219b3f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -35,7 +35,8 @@ internal class SettingsViewModel @Inject constructor( private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, private val getAccessPortRange: GetAccessPortRangeUseCase, private val observeProximityLockEnabled: ObserveIsCurrentProximityLockEnabledUseCase, -) : ViewModel() { + +) : ViewModel(){ private val currentLanguageFlow = MutableStateFlow>(ResourceState.Loading) private val currentGraphSizeFlow = MutableStateFlow>(ResourceState.Loading) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt new file mode 100644 index 00000000..d59ae9a7 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import kotlinx.coroutines.flow.StateFlow + +interface AccessPortDelegate { + val currentAccessPortFlow: StateFlow> +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt new file mode 100644 index 00000000..8c19b5eb --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig +import kotlinx.coroutines.flow.StateFlow + +interface GraphSizeDelegate { + val graphSizeStateFlow: StateFlow> +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt new file mode 100644 index 00000000..e349d454 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt @@ -0,0 +1,13 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import kotlinx.coroutines.flow.StateFlow +import java.util.Locale + +interface LanguageSettingsDelegate { + val languageStateFlow: StateFlow> +} + + + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt new file mode 100644 index 00000000..1bfc45e0 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import kotlinx.coroutines.flow.StateFlow + +interface ProximityLockDelegate { + val proximityLockEnabledStateFlow: StateFlow> +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt new file mode 100644 index 00000000..58ad6205 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import kotlinx.coroutines.flow.StateFlow + +interface UiModeDelegate { + val uiModeStateFlow: StateFlow>> +} + From d41a136b0e93b4166c6fe412697cf9119d22e537 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 12:57:27 +0100 Subject: [PATCH 059/111] rename the setter usecases with update prefix --- ...etCurrentAccessPortUseCase.kt => UpdateAccessPortUseCase.kt} | 2 +- .../{SetGraphSizeUseCase.kt => UpdateGraphSizeUseCase.kt} | 2 +- .../{SetAppLanguageUseCase.kt => UpdateAppLanguageUseCase.kt} | 2 +- ...LockStatusUseCase.kt => UpdateProximityLockStatusUseCase.kt} | 2 +- ...rentAppUiModeUseCase.kt => UpdateCurrentAppUiModeUseCase.kt} | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/{SetCurrentAccessPortUseCase.kt => UpdateAccessPortUseCase.kt} (68%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/{SetGraphSizeUseCase.kt => UpdateGraphSizeUseCase.kt} (71%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/{SetAppLanguageUseCase.kt => UpdateAppLanguageUseCase.kt} (69%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/{SetProximityLockStatusUseCase.kt => UpdateProximityLockStatusUseCase.kt} (66%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/{SetCurrentAppUiModeUseCase.kt => UpdateCurrentAppUiModeUseCase.kt} (66%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt similarity index 68% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index ae7b3e82..83696818 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/SetCurrentAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport -class SetCurrentAccessPortUseCase { +class UpdateAccessPortUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt similarity index 71% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt index 35a59d75..ec476748 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/SetGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -class SetGraphSizeUseCase { +class UpdateGraphSizeUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt similarity index 69% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt index a8d7484d..9f949994 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/SetAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language -class SetAppLanguageUseCase { +class UpdateAppLanguageUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt similarity index 66% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt index c1950dc6..7da0f5e4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/SetProximityLockStatusUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock -class SetProximityLockStatusUseCase { +class UpdateProximityLockStatusUseCase { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt similarity index 66% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt index c4bc02f0..041dd3b9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/SetCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt @@ -1,4 +1,4 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class SetCurrentAppUiModeUseCase { +class UpdateCurrentAppUiModeUseCase { } From ec3122d4c5113ba55691f20797f0440bdd527f49 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 12:58:15 +0100 Subject: [PATCH 060/111] rename ResourceState to UiResourceState.kt --- .../java/de/rwth_aachen/phyphox/utils/UiResourceState.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt b/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt new file mode 100644 index 00000000..59a58dd7 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt @@ -0,0 +1,6 @@ +package de.rwth_aachen.phyphox.utils + +sealed interface UiResourceState { + data object Loading : UiResourceState + data class Success(val data: T) : UiResourceState +} From 2cf00380e765e2afa3b7a08676d357f3da831e67 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 12:58:45 +0100 Subject: [PATCH 061/111] define delegates --- .../viewmodel/delegates/AccessPortDelegate.kt | 9 --------- .../viewmodel/delegates/accessport/AccessPortDelegate.kt | 9 +++++++++ .../delegates/applanguage/AppLanguageDelegate.kt | 9 +++++++++ .../viewmodel/delegates/appuimode/AppUiModeDelegate.kt | 8 ++++++++ .../delegates/proximitylock/ProximityLockDelegate.kt | 8 ++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt deleted file mode 100644 index d59ae9a7..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/AccessPortDelegate.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates - -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import kotlinx.coroutines.flow.StateFlow - -interface AccessPortDelegate { - val currentAccessPortFlow: StateFlow> -} - diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt new file mode 100644 index 00000000..f6fae2ff --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport + +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow + +interface AccessPortDelegate { + val accessPortFlow: Flow> +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt new file mode 100644 index 00000000..f9418848 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage + +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import java.util.Locale + +interface AppLanguageDelegate { + val appLanguageFlow: Flow> +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt new file mode 100644 index 00000000..db714a14 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode + +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow + +interface AppUiModeDelegate { + val appUiModeFlow: Flow>> +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt new file mode 100644 index 00000000..6628e43f --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock + +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow + +interface ProximityLockDelegate { + val proximityLockFlow: Flow> +} From be6caf96bf61f06e6538f0da38bb39eaa1d07d85 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 12:59:08 +0100 Subject: [PATCH 062/111] declare default delegate implementations --- .../accessport/DefaultAccessPortDelegate.kt | 20 +++++++++ .../applanguage/DefaultAppLanguageDelegate.kt | 20 +++++++++ .../appuimode/DefaultAppUiModeDelegate.kt | 42 +++++++++++++++++++ .../graphsize/DefaultGraphSizeDelegate.kt | 41 ++++++++++++++++++ .../DefaultProximityLockDelegate.kt | 18 ++++++++ 5 files changed, 141 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt new file mode 100644 index 00000000..c2741ef0 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -0,0 +1,20 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport + +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +class DefaultAccessPortDelegate @Inject constructor( + private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, + private val getAccessPortRange: GetAccessPortRangeUseCase, + private val updateAccessPort: UpdateAccessPortUseCase +) : AccessPortDelegate { + private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) + override val accessPortFlow: Flow> = currentAccessPortStateFlow + +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt new file mode 100644 index 00000000..95802033 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -0,0 +1,20 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage + +import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.GetSupportedLanguagesUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.UpdateAppLanguageUseCase +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import java.util.Locale +import javax.inject.Inject + +class DefaultAppLanguageDelegate @Inject constructor( + private val observeCurrentAppLanguage: ObserveCurrentAppLanguageUseCase, + private val getSupportedLanguages: GetSupportedLanguagesUseCase, + private val updateAppLanguage: UpdateAppLanguageUseCase +) : AppLanguageDelegate { + private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) + override val appLanguageFlow: Flow> = currentLanguageFlow.asStateFlow() +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt new file mode 100644 index 00000000..7e022867 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt @@ -0,0 +1,42 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.UpdateCurrentAppUiModeUseCase +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import javax.inject.Inject + +class DefaultAppUiModeDelegate @Inject constructor( + private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, + private val getSupportedUiModes: GetSupportedAppUiModeUseCase, + private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase +) : AppUiModeDelegate { + private val currentUiModeUiModelFlow = MutableStateFlow>(UiResourceState.Loading) + private val supportedUiModesFlowUiModel = + MutableStateFlow>>(UiResourceState.Loading) + + override val appUiModeFlow: Flow>> = + combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> + if (current is UiResourceState.Success && modes is UiResourceState.Success) { + UiResourceState.Success( + modes.data.map { mode -> + AppUiModeState( + appUiMode = mode, + isSelected = current.data == mode, + ) + }, + ) + } else { + UiResourceState.Loading + } + } +} + +data class AppUiModeState( + val appUiMode: AppUiMode, + val isSelected: Boolean, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt new file mode 100644 index 00000000..957c3ac2 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt @@ -0,0 +1,41 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize + +import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.GetGraphSizeRangeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.ObserveCurrentGraphSizeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.UpdateGraphSizeUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import javax.inject.Inject + +class DefaultGraphSizeDelegate @Inject constructor( + private val observeCurrentGraphSize: ObserveCurrentGraphSizeUseCase, + private val getGraphSizeRange: GetGraphSizeRangeUseCase, + private val updateGraphSize: UpdateGraphSizeUseCase, +) : GraphSizeDelegate { + private val currentGraphSizeFlow = MutableStateFlow>( + UiResourceState.Loading, + ) + private val graphSizeRangeFlow = + MutableStateFlow>>( + UiResourceState.Loading, + ) + + override val graphSizeFlow: Flow> = combine( + currentGraphSizeFlow, + graphSizeRangeFlow, + ) { graphSize, range -> + if (graphSize is UiResourceState.Success && range is UiResourceState.Success) { + UiResourceState.Success( + SeekBarConfig( + currentSize = graphSize.data, + range = range.data, + ), + ) + } else { + UiResourceState.Loading + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt new file mode 100644 index 00000000..c4b09d8e --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt @@ -0,0 +1,18 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock + +import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.ObserveIsCurrentProximityLockEnabledUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.UpdateProximityLockStatusUseCase +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +class DefaultProximityLockDelegate @Inject constructor( + private val observeProximityLockEnabled: ObserveIsCurrentProximityLockEnabledUseCase, + private val updateProximityLockEnabled: UpdateProximityLockStatusUseCase, +) : ProximityLockDelegate { + + private val proximityLockEnabledFlow = MutableStateFlow>(UiResourceState.Loading) + + override val proximityLockFlow: Flow> = proximityLockEnabledFlow +} From 0d94d59a6463ab59502b1ead0af682a0dcc7ebff Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 12:59:32 +0100 Subject: [PATCH 063/111] declare default delegate implementations --- .../viewmodel/delegates/GraphSizeDelegate.kt | 10 ---------- .../viewmodel/delegates/graphsize/GraphSizeDelegate.kt | 9 +++++++++ 2 files changed, 9 insertions(+), 10 deletions(-) delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt deleted file mode 100644 index 8c19b5eb..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/GraphSizeDelegate.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates - -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig -import kotlinx.coroutines.flow.StateFlow - -interface GraphSizeDelegate { - val graphSizeStateFlow: StateFlow> -} - diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt new file mode 100644 index 00000000..dca18201 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize + +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.flow.Flow + +interface GraphSizeDelegate { + val graphSizeFlow: Flow> +} From 6661fa5fd9d18cc63fcf7dbbefe10db78858209f Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 13:00:17 +0100 Subject: [PATCH 064/111] update ClickablePreferenceItem.kt --- .../ClickablePreferenceItem.kt | 12 ++++++------ .../ClickablePreferenceItemPreviewProvider.kt | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt index c923fa19..c671d74c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -13,31 +13,31 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import de.rwth_aachen.phyphox.utils.UiResourceState @Composable fun ClickablePreferenceItem( modifier: Modifier = Modifier, title: StringUIModel, - summary: ResourceState, + summary: UiResourceState, iconRes: Int? = null, onClick: () -> Unit = {}, ) { PreferenceItem( modifier = modifier.clickable( - enabled = summary is ResourceState.Success, + enabled = summary is UiResourceState.Success, onClick = onClick, ), title = title, iconRes = iconRes, content = { when (summary) { - ResourceState.Loading -> Box( + UiResourceState.Loading -> Box( modifier = Modifier .padding(top = 4.dp) .fillMaxWidth() @@ -45,7 +45,7 @@ fun ClickablePreferenceItem( .skeleton(), ) - is ResourceState.Success -> PreferenceSummaryItem( + is UiResourceState.Success -> PreferenceSummaryItem( text = summary.data, ) } @@ -56,7 +56,7 @@ fun ClickablePreferenceItem( @Preview(showBackground = true) @Composable internal fun ClickablePreferenceItemPreview( - @PreviewParameter(ClickablePreferenceItemPreviewProvider::class) preview: ResourceState, + @PreviewParameter(ClickablePreferenceItemPreviewProvider::class) preview: UiResourceState, ) { PhyphoxTheme { Surface { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt index 624ae8ad..80e2e3de 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt @@ -1,14 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.utils.UiResourceState -class ClickablePreferenceItemPreviewProvider : PreviewParameterProvider> { - override val values: Sequence> +class ClickablePreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> get() = sequenceOf( - ResourceState.Loading, - ResourceState.Success(LoremIpsumStringUIModel(8)), + UiResourceState.Loading, + UiResourceState.Success(LoremIpsumStringUIModel(8)), ) } From 428871500fc74ceed5f12148655adc6d0fc20cc8 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 13:01:19 +0100 Subject: [PATCH 065/111] move UiResourceState.kt extension --- .../main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt b/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt index 59a58dd7..9a9417d9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/utils/UiResourceState.kt @@ -4,3 +4,7 @@ sealed interface UiResourceState { data object Loading : UiResourceState data class Success(val data: T) : UiResourceState } + +fun UiResourceState.isChecked(): Boolean { + return this is UiResourceState.Success && this.data +} From 50aa9b4fdc797681a9251402e27025f3042fae79 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 13:01:34 +0100 Subject: [PATCH 066/111] remove old delgates --- .../viewmodel/delegates/LanguageSettingsDelegate.kt | 13 ------------- .../viewmodel/delegates/ProximityLockDelegate.kt | 8 -------- .../viewmodel/delegates/UiModeDelegate.kt | 10 ---------- 3 files changed, 31 deletions(-) delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt deleted file mode 100644 index e349d454..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/LanguageSettingsDelegate.kt +++ /dev/null @@ -1,13 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates - - -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import kotlinx.coroutines.flow.StateFlow -import java.util.Locale - -interface LanguageSettingsDelegate { - val languageStateFlow: StateFlow> -} - - - diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt deleted file mode 100644 index 1bfc45e0..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/ProximityLockDelegate.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates - -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import kotlinx.coroutines.flow.StateFlow - -interface ProximityLockDelegate { - val proximityLockEnabledStateFlow: StateFlow> -} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt deleted file mode 100644 index 58ad6205..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/UiModeDelegate.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates - -import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import kotlinx.coroutines.flow.StateFlow - -interface UiModeDelegate { - val uiModeStateFlow: StateFlow>> -} - From 846ab7ab4243f3cee4d9430a153df8a87e28e41f Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 13:01:52 +0100 Subject: [PATCH 067/111] define uicomponent configs --- .../seekbarpreferenceitem/SeekBarConfig.kt | 15 +++++++++++++++ .../SegmentedButtonConfig.kt | 14 ++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt new file mode 100644 index 00000000..64bb4e53 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt @@ -0,0 +1,15 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem + +data class SeekBarConfig( + val currentSize: Float, + val range: ClosedFloatingPointRange, +) { + constructor( + currentSize: Float, + minSize: Float, + maxSize: Float, + ) : this( + currentSize = currentSize, + range = minSize..maxSize, + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt new file mode 100644 index 00000000..f25c3f4b --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt @@ -0,0 +1,14 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class SegmentedButtonConfig( + val item: T, + val text: StringUIModel, +) + +data class SegmentedButtonsPreferenceItemConfig( + val selectedItem: SegmentedButtonConfig, + val options: List>, +) From 941d472b97711840360e044c560bae26e4cd8f11 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 13:23:30 +0100 Subject: [PATCH 068/111] update the logic to have the delegates build the uimodels as well --- .../presentation/compose/SettingsRoot.kt | 2 +- .../SeekBarPreferenceItem.kt | 11 +- .../SeekBarPreferenceItemPreviewProvider.kt | 11 +- .../SegmentedButtonConfig.kt | 14 -- .../SegmentedButtonPreferenceItem.kt | 33 +++-- .../SegmentedButtonUiModel.kt | 9 ++ .../settingscontent/SettingsContent.kt | 28 ++-- .../SwitchPreferenceItem.kt | 12 +- .../SwitchPreferenceItemPreviewProvider.kt | 12 +- .../presentation/viewmodel/SettingsAction.kt | 6 +- .../viewmodel/SettingsSheetUiModel.kt | 11 ++ .../presentation/viewmodel/SettingsUiState.kt | 53 ++------ .../viewmodel/SettingsViewModel.kt | 126 ++++-------------- .../presentation/viewmodel/UiBuilder.kt | 48 +++++-- .../delegates/appuimode/AppUiModeDelegate.kt | 4 +- .../appuimode/DefaultAppUiModeDelegate.kt | 26 +--- 16 files changed, 159 insertions(+), 247 deletions(-) delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 5caa905e..4dbe5317 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -50,7 +50,7 @@ fun SettingsRoot( modifier = modifier.padding(innerPadding), currentLanguage = uiState.currentLanguage, seekbarConfig = uiState.graphSize, - uiModeUiModel = uiState.uiModeUiModel, + segmentedButtonUiModel = uiState.segmentedButtonUiModel, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, onActionEvent = onActionEvent, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 554add51..9fa3f3e0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -13,12 +13,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import de.rwth_aachen.phyphox.utils.UiResourceState @Composable fun SeekBarPreferenceItem( @@ -26,7 +25,7 @@ fun SeekBarPreferenceItem( title: StringUIModel, iconRes: Int? = null, summary: StringUIModel? = null, - seekBarConfig: ResourceState, + seekBarConfig: UiResourceState, onValueChange: (Float) -> Unit, ) { PreferenceItem( @@ -38,7 +37,7 @@ fun SeekBarPreferenceItem( PreferenceSummaryItem(text = summary) } when (seekBarConfig) { - ResourceState.Loading -> Box( + UiResourceState.Loading -> Box( modifier = Modifier .padding(top = 4.dp) .fillMaxWidth() @@ -46,7 +45,7 @@ fun SeekBarPreferenceItem( .skeleton(), ) - is ResourceState.Success -> Slider( + is UiResourceState.Success -> Slider( value = seekBarConfig.data.currentSize, onValueChange = onValueChange, valueRange = seekBarConfig.data.range, @@ -60,7 +59,7 @@ fun SeekBarPreferenceItem( @Preview(showBackground = true) @Composable internal fun SeekBarPreferenceItemPreview( - @PreviewParameter(SeekBarPreferenceItemPreviewProvider::class) graphSize: ResourceState, + @PreviewParameter(SeekBarPreferenceItemPreviewProvider::class) graphSize: UiResourceState, ) { PhyphoxTheme { Surface { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt index 709b6d4e..b6aa9414 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt @@ -1,14 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.utils.UiResourceState -class SeekBarPreferenceItemPreviewProvider : PreviewParameterProvider> { - override val values: Sequence> +class SeekBarPreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> get() = sequenceOf( - ResourceState.Loading, - ResourceState.Success( + UiResourceState.Loading, + UiResourceState.Success( SeekBarConfig( currentSize = 1f, minSize = 0f, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt deleted file mode 100644 index f25c3f4b..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonConfig.kt +++ /dev/null @@ -1,14 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem - -import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.ui.string.StringUIModel - -data class SegmentedButtonConfig( - val item: T, - val text: StringUIModel, -) - -data class SegmentedButtonsPreferenceItemConfig( - val selectedItem: SegmentedButtonConfig, - val options: List>, -) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index 810ba8cc..ccc28746 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -16,13 +16,12 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import de.rwth_aachen.phyphox.utils.UiResourceState @Composable fun SegmentedButtonPreferenceItem( @@ -30,8 +29,8 @@ fun SegmentedButtonPreferenceItem( title: StringUIModel, summary: StringUIModel? = null, iconRes: Int? = null, - options: ResourceState>, - onOptionSelected: (UiModeUiModel) -> Unit, + config: UiResourceState>>, + onOptionSelected: (SegmentedButtonUiModel) -> Unit, ) { PreferenceItem( modifier = modifier, @@ -41,8 +40,8 @@ fun SegmentedButtonPreferenceItem( summary?.let { PreferenceSummaryItem(text = summary) } - when (options) { - ResourceState.Loading -> Box( + when (config) { + UiResourceState.Loading -> Box( modifier = Modifier .padding(top = 4.dp) .fillMaxWidth() @@ -50,14 +49,14 @@ fun SegmentedButtonPreferenceItem( .skeleton(), ) - is ResourceState.Success> -> SingleChoiceSegmentedButtonRow( + is UiResourceState.Success>> -> SingleChoiceSegmentedButtonRow( modifier = Modifier.fillMaxWidth(), ) { - options.data.forEachIndexed { index, option -> + config.data.forEachIndexed { index, option -> SegmentedButton( shape = SegmentedButtonDefaults.itemShape( index = index, - count = options.data.size, + count = config.data.size, ), onClick = { onOptionSelected(option) @@ -81,20 +80,20 @@ internal fun SegmentedButtonPreferenceItemPreview() { title = LoremIpsumStringUIModel(4), summary = LoremIpsumStringUIModel(12), iconRes = R.drawable.ic_dark_mode, - options = ResourceState.Success( - listOf( - UiModeUiModel( - appUiMode = AppUiMode.DARK, + config = UiResourceState.Success( + data = listOf( + SegmentedButtonUiModel( + item = AppUiMode.DARK, text = LoremIpsumStringUIModel(2), isSelected = true, ), - UiModeUiModel( - appUiMode = AppUiMode.SYSTEM, + SegmentedButtonUiModel( + item = AppUiMode.SYSTEM, text = LoremIpsumStringUIModel(1), isSelected = false, ), - UiModeUiModel( - appUiMode = AppUiMode.LIGHT, + SegmentedButtonUiModel( + item = AppUiMode.LIGHT, text = LoremIpsumStringUIModel(1), isSelected = false, ), diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt new file mode 100644 index 00000000..a7a9b0e8 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem + +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class SegmentedButtonUiModel( + val item: T, + val text: StringUIModel, + val isSelected: Boolean = false, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 7ed2d3a7..e93f6d50 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -25,10 +25,10 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencec import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiModeUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SegmentedButtonUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -37,11 +37,11 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun SettingsContent( modifier: Modifier = Modifier, - currentLanguage: ResourceState, - seekbarConfig: ResourceState, - uiModeUiModel: ResourceState>, - accessPort: ResourceState, - proximityLockEnabled: ResourceState, + currentLanguage: UiResourceState, + seekbarConfig: UiResourceState, + segmentedButtonUiModel: UiResourceState>, + accessPort: UiResourceState, + proximityLockEnabled: UiResourceState, onActionEvent: (SettingsAction) -> Unit, ) { LazyColumn( @@ -84,7 +84,7 @@ fun SettingsContent( item { ClickablePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsTranslation), - summary = ResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), + summary = UiResourceState.Success(ResourceStringUIModel(R.string.settingsTranslationMore)), iconRes = R.drawable.setting_translate, onClick = { onActionEvent(SettingsAction.OnLearnMoreAboutTranslationClicked) @@ -113,7 +113,7 @@ fun SettingsContent( item { SegmentedButtonPreferenceItem( title = ResourceStringUIModel(resId = R.string.settings_theme_title), - options = uiModeUiModel, + config = segmentedButtonUiModel, iconRes = R.drawable.ic_dark_mode, onOptionSelected = { onActionEvent(SettingsAction.OnUiModeItemSelected(it)) @@ -168,11 +168,11 @@ fun SettingsContentPreview() { PhyphoxTheme { Surface { SettingsContent( - currentLanguage = ResourceState.Loading, - seekbarConfig = ResourceState.Loading, - uiModeUiModel = ResourceState.Loading, - accessPort = ResourceState.Loading, - proximityLockEnabled = ResourceState.Loading, + currentLanguage = UiResourceState.Loading, + seekbarConfig = UiResourceState.Loading, + segmentedButtonUiModel = UiResourceState.Loading, + accessPort = UiResourceState.Loading, + proximityLockEnabled = UiResourceState.Loading, onActionEvent = {}, ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index eb4a3049..d55649d0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel @@ -29,13 +29,13 @@ fun SwitchPreferenceItem( title: StringUIModel, summary: StringUIModel? = null, iconRes: Int? = null, - checked: ResourceState, + checked: UiResourceState, onCheckedChange: (Boolean) -> Unit, ) { PreferenceItem( modifier = modifier - .clickable(checked is ResourceState.Success) { + .clickable(checked is UiResourceState.Success) { onCheckedChange(checked.isChecked()) }, title = title, @@ -47,7 +47,7 @@ fun SwitchPreferenceItem( }, trailingContent = { when (checked) { - ResourceState.Loading -> Box( + UiResourceState.Loading -> Box( modifier = Modifier .padding(top = 16.dp) .width(48.dp) @@ -55,7 +55,7 @@ fun SwitchPreferenceItem( .skeleton(), ) - is ResourceState.Success -> + is UiResourceState.Success -> Switch( checked = checked.isChecked(), onCheckedChange = onCheckedChange, @@ -75,7 +75,7 @@ fun SwitchPreferenceItem( ) @Composable internal fun SwitchPreferenceItemPreview( - @PreviewParameter(SwitchPreferenceItemPreviewProvider::class) value: ResourceState, + @PreviewParameter(SwitchPreferenceItemPreviewProvider::class) value: UiResourceState, ) { PhyphoxTheme { Surface { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt index 4d337054..998d19cb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt @@ -1,13 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.ResourceState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState -class SwitchPreferenceItemPreviewProvider : PreviewParameterProvider> { - override val values: Sequence> +class SwitchPreferenceItemPreviewProvider : PreviewParameterProvider> { + override val values: Sequence> get() = sequenceOf( - ResourceState.Loading, - ResourceState.Success(true), - ResourceState.Success(false), + UiResourceState.Loading, + UiResourceState.Success(true), + UiResourceState.Success(false), ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt index 473e5e6a..d347b4bb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -1,7 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel + sealed interface SettingsAction { - data class OnUiModeItemSelected(val appUiMode: UiModeUiModel) : SettingsAction + + data class OnUiModeItemSelected(val appUiMode: SegmentedButtonUiModel) : SettingsAction data class OnGraphSizeChanged(val size: Float) : SettingsAction data class OnProximityLockChanged(val enabled: Boolean) : SettingsAction data object OnAppLanguageClicked : SettingsAction diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt new file mode 100644 index 00000000..41161499 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt @@ -0,0 +1,11 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel + +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +sealed interface SettingsSheetUiModel { + data class AccessPortSheetUiModel( + val currentPort: StringUIModel, + val range: ClosedFloatingPointRange, + val error: StringUIModel? = null, + ) : SettingsSheetUiModel +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index b3aa5d49..e289689b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -1,52 +1,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.utils.UiResourceState data class SettingsUiState( - val currentLanguage: ResourceState = ResourceState.Loading, - val graphSize: ResourceState = ResourceState.Loading, - val uiModeUiModel: ResourceState> = ResourceState.Loading, + val currentLanguage: UiResourceState = UiResourceState.Loading, + val graphSize: UiResourceState = UiResourceState.Loading, + val segmentedButtonUiModel: UiResourceState>> = UiResourceState.Loading, // val dynamicTheme: ResourceState = ResourceState.Loading, - val accessPort: ResourceState = ResourceState.Loading, - val proximityLockEnabled: ResourceState = ResourceState.Loading, - val modal: SettingsModal? = null, + val accessPort: UiResourceState = UiResourceState.Loading, + val proximityLockEnabled: UiResourceState = UiResourceState.Loading, + val modal: SettingsSheetUiModel? = null, ) - -sealed interface ResourceState { - data object Loading : ResourceState - data class Success(val data: T) : ResourceState -} - -fun ResourceState.isChecked(): Boolean { - return this is ResourceState.Success && this.data -} - -data class SeekBarConfig( - val currentSize: Float, - val range: ClosedFloatingPointRange, -) { - constructor( - currentSize: Float, - minSize: Float, - maxSize: Float, - ) : this( - currentSize = currentSize, - range = minSize..maxSize, - ) -} - -data class UiModeUiModel( - val appUiMode: AppUiMode, - val text: StringUIModel, - val isSelected: Boolean, -) - -sealed interface SettingsModal { - data class AccessPortModal( - val currentPort: StringUIModel, - val range: ClosedFloatingPointRange, - val error: StringUIModel? = null, - ) : SettingsModal - -} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index b9219b3f..b0666dea 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -3,131 +3,51 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.GetGraphSizeRangeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.ObserveCurrentGraphSizeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.ObserveIsCurrentProximityLockEnabledUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase -import de.rwth_aachen.phyphox.ui.string.StringUIModel -import de.rwth_aachen.phyphox.ui.string.TextStringUIModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate import de.rwth_aachen.phyphox.utils.UIEventFlow import de.rwth_aachen.phyphox.utils.asFlow -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel internal class SettingsViewModel @Inject constructor( - private val uiBuilder: UiBuilder, - private val observeCurrentAppLanguage: ObserveCurrentAppLanguageUseCase, - private val observeCurrentGraphSize: ObserveCurrentGraphSizeUseCase, - private val getGraphSizeRange: GetGraphSizeRangeUseCase, - private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, - private val getSupportedUiModes: GetSupportedAppUiModeUseCase, - private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, - private val getAccessPortRange: GetAccessPortRangeUseCase, - private val observeProximityLockEnabled: ObserveIsCurrentProximityLockEnabledUseCase, + private val accessPortDelegate: AccessPortDelegate, + private val appLanguageDelegate: AppLanguageDelegate, + private val appUiModeDelegate: AppUiModeDelegate, + private val graphSizeDelegate: GraphSizeDelegate, + private val proximityLockDelegate: ProximityLockDelegate, +) : ViewModel(), + AccessPortDelegate by accessPortDelegate, + AppLanguageDelegate by appLanguageDelegate, + AppUiModeDelegate by appUiModeDelegate, + GraphSizeDelegate by graphSizeDelegate, + ProximityLockDelegate by proximityLockDelegate { -) : ViewModel(){ - - private val currentLanguageFlow = MutableStateFlow>(ResourceState.Loading) - private val currentGraphSizeFlow = MutableStateFlow>(ResourceState.Loading) - private val graphSizeRangeFlow = - MutableStateFlow>>(ResourceState.Loading) - private val currentUiModeUiModelFlow = MutableStateFlow>(ResourceState.Loading) - private val supportedUiModesFlowUiModel = MutableStateFlow>>(ResourceState.Loading) - private val currentAccessPortFlow = MutableStateFlow>(ResourceState.Loading) - private val proximityLockEnabledFlow = MutableStateFlow>(ResourceState.Loading) private val _uiEvent = UIEventFlow() val uiEvent = _uiEvent.asFlow() - private val graphSizeFlow = combine(currentGraphSizeFlow, graphSizeRangeFlow) { graphSize, range -> - if (graphSize is ResourceState.Success && range is ResourceState.Success) { - ResourceState.Success( - data = uiBuilder.buildSeekBarConfig( - currentSize = graphSize.data, - range = range.data, - ), - ) - } else { - ResourceState.Loading - } - } - - private val uiModeFlow = combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> - if (current is ResourceState.Success && modes is ResourceState.Success) { - ResourceState.Success( - uiBuilder.buildUiModeUiModels( - currentUiModeUiMode = current.data, - supportedModes = modes.data, - ), - ) - } else { - ResourceState.Loading - } - } val uiState = combine( - currentLanguageFlow, - graphSizeFlow, - uiModeFlow, - currentAccessPortFlow, - proximityLockEnabledFlow, - ) { currentLanguage, graphSize, uiMode, accessPort, proximityLockEnabled -> - SettingsUiState( - currentLanguage = currentLanguage, - graphSize = graphSize, - uiModeUiModel = uiMode, - accessPort = accessPort, - proximityLockEnabled = proximityLockEnabled, - ) + accessPortDelegate.accessPortFlow, + appLanguageDelegate.appLanguageFlow, + appUiModeDelegate.appUiModeFlow, + graphSizeDelegate.graphSizeFlow, + proximityLockDelegate.proximityLockFlow, + ) { accessPort, currentLanguage, uiMode, graphSize, proximityLockEnabled -> + }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = SettingsUiState(), ) - init { - loadDummyData() - } - - private fun loadDummyData() = viewModelScope.launch { - delay(800) - currentLanguageFlow.value = ResourceState.Success(TextStringUIModel("English")) - delay(1200) - currentGraphSizeFlow.value = ResourceState.Success( - data = 1.0f, - ) - delay(200) - graphSizeRangeFlow.value = ResourceState.Success( - data = 0.0f..3.0f, - ) - delay(800) - currentUiModeUiModelFlow.value = ResourceState.Success( - data = AppUiMode.DARK, - ) - supportedUiModesFlowUiModel.value = ResourceState.Success( - AppUiMode.entries, - ) - delay(600) - currentAccessPortFlow.value = ResourceState.Success( - data = TextStringUIModel("8080"), - ) - delay(2000) - proximityLockEnabledFlow.value = ResourceState.Success( - data = true, - ) - } - fun onActionEvent(action: SettingsAction) { } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index d6f8b066..aad459c1 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -2,11 +2,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.toStringUIModel +import de.rwth_aachen.phyphox.utils.UiResourceState +import java.util.Locale import javax.inject.Inject internal class UiBuilder @Inject constructor() { + fun buildSeekBarConfig( currentSize: Float, range: ClosedFloatingPointRange, @@ -18,16 +24,39 @@ internal class UiBuilder @Inject constructor() { ) } - fun buildUiModeUiModels( - currentUiModeUiMode: AppUiMode, - supportedModes: List, - ): List { - return supportedModes.map { mode -> - UiModeUiModel( - appUiMode = mode, - text = getSupportedUiModeText(mode), - isSelected = mode == currentUiModeUiMode, + + //region - App Language + private fun buildLanguageUiModel(localeResource: UiResourceState): UiResourceState { + return when (localeResource) { + UiResourceState.Loading -> UiResourceState.Loading + is UiResourceState.Success -> UiResourceState.Success( + mapLocaleToUiModel(localeResource.data), + ) + } + } + + private fun mapLocaleToUiModel(locale: Locale): StringUIModel { + return locale.displayName.toStringUIModel() + } + //endregion + + //region - AppUiMode + fun buildAppUiModeResource( + current: UiResourceState, + modes: UiResourceState>, + ): UiResourceState>> { + return if (current is UiResourceState.Success && modes is UiResourceState.Success) { + UiResourceState.Success( + modes.data.map { mode -> + SegmentedButtonUiModel( + item = mode, + isSelected = current.data == mode, + text = getSupportedUiModeText(mode), + ) + }, ) + } else { + UiResourceState.Loading } } @@ -38,4 +67,5 @@ internal class UiBuilder @Inject constructor() { AppUiMode.DARK -> ResourceStringUIModel(R.string.settings_mode_dark) } } + //endregion } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt index db714a14..af45872e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt @@ -1,8 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow interface AppUiModeDelegate { - val appUiModeFlow: Flow>> + val appUiModeFlow: Flow>>> } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt index 7e022867..8f5ab3a4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt @@ -4,39 +4,27 @@ import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.UpdateCurrentAppUiModeUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import javax.inject.Inject -class DefaultAppUiModeDelegate @Inject constructor( +internal class DefaultAppUiModeDelegate @Inject constructor( private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, private val getSupportedUiModes: GetSupportedAppUiModeUseCase, - private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase + private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase, + private val uiBuilder: UiBuilder ) : AppUiModeDelegate { private val currentUiModeUiModelFlow = MutableStateFlow>(UiResourceState.Loading) private val supportedUiModesFlowUiModel = MutableStateFlow>>(UiResourceState.Loading) - override val appUiModeFlow: Flow>> = + override val appUiModeFlow: Flow>>> = combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> - if (current is UiResourceState.Success && modes is UiResourceState.Success) { - UiResourceState.Success( - modes.data.map { mode -> - AppUiModeState( - appUiMode = mode, - isSelected = current.data == mode, - ) - }, - ) - } else { - UiResourceState.Loading - } + uiBuilder.buildAppUiModeResource(current, modes) } } -data class AppUiModeState( - val appUiMode: AppUiMode, - val isSelected: Boolean, -) From 91e1ae4bd840814a591f9c45b37fec0a953c1378 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 14:08:20 +0100 Subject: [PATCH 069/111] define proxy enabled delegate --- ...rveIsCurrentProximityLockEnabledUseCase.kt | 9 ++++++++- .../UpdateProximityLockStatusUseCase.kt | 7 ++++++- .../DefaultProximityLockDelegate.kt | 19 +++++++++++++++++++ .../proximitylock/ProximityLockDelegate.kt | 5 +++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt index 5df876da..11fa1f9b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt @@ -1,4 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock -class ObserveIsCurrentProximityLockEnabledUseCase { +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +class ObserveIsCurrentProximityLockEnabledUseCase @Inject constructor(){ + operator fun invoke(): Flow { + return flowOf(true) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt index 7da0f5e4..3143a215 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt @@ -1,4 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock -class UpdateProximityLockStatusUseCase { +import javax.inject.Inject + +class UpdateProximityLockStatusUseCase @Inject constructor(){ + suspend operator fun invoke(enabled: Boolean) : Result{ + return Result.success(Unit) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt index c4b09d8e..3197a1c7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt @@ -3,8 +3,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.ObserveIsCurrentProximityLockEnabledUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock.UpdateProximityLockStatusUseCase import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import javax.inject.Inject class DefaultProximityLockDelegate @Inject constructor( @@ -15,4 +19,19 @@ class DefaultProximityLockDelegate @Inject constructor( private val proximityLockEnabledFlow = MutableStateFlow>(UiResourceState.Loading) override val proximityLockFlow: Flow> = proximityLockEnabledFlow + + override fun start(scope: CoroutineScope) { + observeProximityLockEnabled() + .onStart { + proximityLockEnabledFlow.value = UiResourceState.Loading + } + .onEach { + proximityLockEnabledFlow.value = UiResourceState.Success(it) + }.launchIn(scope) + } + + + override suspend fun updateProximityLockStatus(enabled: Boolean) { + updateProximityLockEnabled(enabled) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt index 6628e43f..a70d0b78 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt @@ -1,8 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface ProximityLockDelegate { val proximityLockFlow: Flow> + + fun start(scope: CoroutineScope) + + suspend fun updateProximityLockStatus(enabled: Boolean) } From 6a1f2147beb96f09aad9ed6fe35a95b5cb1dd1c8 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 14:24:37 +0100 Subject: [PATCH 070/111] define proxy enabled delegate --- .../viewmodel/delegates/graphsize/GraphSizeDelegate.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt index dca18201..cc947289 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt @@ -2,8 +2,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface GraphSizeDelegate { val graphSizeFlow: Flow> + fun start(scope: CoroutineScope) + suspend fun updateGraphSize(size: Float) } From 1430f3cda24407b7f7f7c5a6c98a7fd098e9b6f6 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 14:56:32 +0100 Subject: [PATCH 071/111] define usecases for graph size --- .../settings/domain/model/GraphConfig.kt | 9 +++++++++ .../settings/domain/model/GraphItemsRange.kt | 8 ++++++++ ...BorderWidthMultiplierRangeDomainService.kt | 9 +++++++++ ...etLabelSizeMultiplierRangeDomainService.kt | 9 +++++++++ ...etLineWidthMultiplierRangeDomainService.kt | 9 +++++++++ ...GetTextSizeMultiplierRangeDomainService.kt | 9 +++++++++ .../ObserveCurrentGraphSizeUseCase.kt | 19 ++++++++++++++++++- 7 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphConfig.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphItemsRange.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetBorderWidthMultiplierRangeDomainService.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLabelSizeMultiplierRangeDomainService.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLineWidthMultiplierRangeDomainService.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetTextSizeMultiplierRangeDomainService.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphConfig.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphConfig.kt new file mode 100644 index 00000000..b766bf2d --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphConfig.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.model + +data class GraphConfig( + val labelSizeMultiplier: Float, + val textSizeMultiplier: Float, + val lineWidthMultiplier: Float, + val borderWidthMultiplier: Float, +) + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphItemsRange.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphItemsRange.kt new file mode 100644 index 00000000..b39f3dfd --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/GraphItemsRange.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.features.settings.domain.model + +data class GraphItemsRange( + val labelSizeRange: ClosedFloatingPointRange = 0.5f..2.5f, + val textSizeRange: ClosedFloatingPointRange = 0.5f..2.5f, + val lineWidthRange: ClosedFloatingPointRange = 0.5f..2.5f, + val borderWidthRange: ClosedFloatingPointRange = 0.5f..2.5f, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetBorderWidthMultiplierRangeDomainService.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetBorderWidthMultiplierRangeDomainService.kt new file mode 100644 index 00000000..33d731fa --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetBorderWidthMultiplierRangeDomainService.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import javax.inject.Inject + +class GetBorderWidthMultiplierRangeDomainService @Inject constructor() { + suspend operator fun invoke(): ClosedFloatingPointRange { + return 0.5f..2.5f + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLabelSizeMultiplierRangeDomainService.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLabelSizeMultiplierRangeDomainService.kt new file mode 100644 index 00000000..ab04ff17 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLabelSizeMultiplierRangeDomainService.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import javax.inject.Inject + +class GetLabelSizeMultiplierRangeDomainService @Inject constructor() { + suspend operator fun invoke(): ClosedFloatingPointRange { + return 0.5f..2.5f + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLineWidthMultiplierRangeDomainService.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLineWidthMultiplierRangeDomainService.kt new file mode 100644 index 00000000..9ef3e57b --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetLineWidthMultiplierRangeDomainService.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import javax.inject.Inject + +class GetLineWidthMultiplierRangeDomainService @Inject constructor() { + suspend operator fun invoke(): ClosedFloatingPointRange { + return 0.5f..2.5f + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetTextSizeMultiplierRangeDomainService.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetTextSizeMultiplierRangeDomainService.kt new file mode 100644 index 00000000..10c81382 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetTextSizeMultiplierRangeDomainService.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import javax.inject.Inject + +class GetTextSizeMultiplierRangeDomainService @Inject constructor() { + suspend operator fun invoke(): ClosedFloatingPointRange { + return 0.5f..2.5f + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt index 6f1cc54a..043fd3ca 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt @@ -1,4 +1,21 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -class ObserveCurrentGraphSizeUseCase { +import de.rwth_aachen.phyphox.features.settings.domain.model.GraphConfig +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +class ObserveCurrentGraphSizeUseCase @Inject constructor( + +) { + operator fun invoke(): Flow { + return flowOf( + GraphConfig( + labelSizeMultiplier = 1.0f, + textSizeMultiplier = 1.0f, + lineWidthMultiplier = 1.0f, + borderWidthMultiplier = 1.0f, + ), + ) + } } From 2e080dc1998afedb74fd280522da4854a5e4b045 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 15:01:22 +0100 Subject: [PATCH 072/111] get graph size range --- .../graphsize/GetGraphSizeRangeUseCase.kt | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt index 580f404e..05b0ba9c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt @@ -1,4 +1,27 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -class GetGraphSizeRangeUseCase { +import de.rwth_aachen.phyphox.features.settings.domain.model.GraphItemsRange +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import javax.inject.Inject + +class GetGraphSizeRangeUseCase @Inject constructor( + private val getLabelSizeMultiplierRange: GetLabelSizeMultiplierRangeDomainService, + private val getTextSizeMultiplierRange: GetTextSizeMultiplierRangeDomainService, + private val getLineWidthMultiplierRange: GetLineWidthMultiplierRangeDomainService, + private val getBorderWidthMultiplierRange: GetBorderWidthMultiplierRangeDomainService, +) { + suspend operator fun invoke(): GraphItemsRange = coroutineScope { + val labelSizeRangeDeferred = async { getLabelSizeMultiplierRange() } + val textSizeRangeDeferred = async { getTextSizeMultiplierRange() } + val lineWidthRangeDeferred = async { getLineWidthMultiplierRange() } + val borderWidthRangeDeferred = async { getBorderWidthMultiplierRange() } + + GraphItemsRange( + labelSizeRange = labelSizeRangeDeferred.await(), + textSizeRange = textSizeRangeDeferred.await(), + lineWidthRange = lineWidthRangeDeferred.await(), + borderWidthRange = borderWidthRangeDeferred.await(), + ) + } } From 24df5003d96aca350a2759848d96b91dbcff3c8d Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 15:08:35 +0100 Subject: [PATCH 073/111] update delegates --- .../presentation/viewmodel/SettingsViewModel.kt | 11 +++++++++++ .../delegates/accessport/AccessPortDelegate.kt | 2 ++ .../delegates/accessport/DefaultAccessPortDelegate.kt | 4 ++++ .../delegates/applanguage/AppLanguageDelegate.kt | 2 ++ .../applanguage/DefaultAppLanguageDelegate.kt | 4 ++++ .../delegates/appuimode/AppUiModeDelegate.kt | 2 ++ .../delegates/appuimode/DefaultAppUiModeDelegate.kt | 5 +++++ .../delegates/graphsize/DefaultGraphSizeDelegate.kt | 9 +++++++++ 8 files changed, 39 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index b0666dea..20781e5e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -10,6 +10,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate import de.rwth_aachen.phyphox.utils.UIEventFlow import de.rwth_aachen.phyphox.utils.asFlow +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn @@ -33,6 +34,16 @@ internal class SettingsViewModel @Inject constructor( private val _uiEvent = UIEventFlow() val uiEvent = _uiEvent.asFlow() + init { + start(viewModelScope) + } + + override fun start(scope: CoroutineScope) { + accessPortDelegate.start(scope) + appLanguageDelegate.start(scope) + appUiModeDelegate.start(scope) + graphSizeDelegate.start(scope) + } val uiState = combine( accessPortDelegate.accessPortFlow, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index f6fae2ff..1a2c93af 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -1,9 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface AccessPortDelegate { val accessPortFlow: Flow> + fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index c2741ef0..c88efc1c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -4,6 +4,7 @@ import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAcc import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @@ -15,6 +16,9 @@ class DefaultAccessPortDelegate @Inject constructor( ) : AccessPortDelegate { private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) override val accessPortFlow: Flow> = currentAccessPortStateFlow + override fun start(scope: CoroutineScope) { + TODO("Not yet implemented") + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt index f9418848..8d54cb35 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt @@ -1,9 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import java.util.Locale interface AppLanguageDelegate { val appLanguageFlow: Flow> + fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index 95802033..0b56db91 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -4,6 +4,7 @@ import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.GetSuppo import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.UpdateAppLanguageUseCase import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,4 +18,7 @@ class DefaultAppLanguageDelegate @Inject constructor( ) : AppLanguageDelegate { private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) override val appLanguageFlow: Flow> = currentLanguageFlow.asStateFlow() + override fun start(scope: CoroutineScope) { + TODO("Not yet implemented") + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt index af45872e..5df31c35 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt @@ -3,8 +3,10 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface AppUiModeDelegate { val appUiModeFlow: Flow>>> + fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt index 8f5ab3a4..7c54139c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt @@ -7,6 +7,7 @@ import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.UpdateCurr import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -26,5 +27,9 @@ internal class DefaultAppUiModeDelegate @Inject constructor( combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> uiBuilder.buildAppUiModeResource(current, modes) } + + override fun start(scope: CoroutineScope) { + TODO("Not yet implemented") + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt index 957c3ac2..9bb661d5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt @@ -5,6 +5,7 @@ import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.Observe import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.UpdateGraphSizeUseCase import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -38,4 +39,12 @@ class DefaultGraphSizeDelegate @Inject constructor( UiResourceState.Loading } } + + override fun start(scope: CoroutineScope) { + TODO("Not yet implemented") + } + + override suspend fun updateGraphSize(size: Float) { + TODO("Not yet implemented") + } } From ebbee21068b893ea200d720404fd915b8d724ce9 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 15:12:01 +0100 Subject: [PATCH 074/111] remove unused import from settings content composable --- .../compose/settingscontent/SettingsContent.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index e93f6d50..4ca0fa0f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -20,18 +20,19 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.ClickablePreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader +import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SegmentedButtonUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import de.rwth_aachen.phyphox.utils.UiResourceState @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -39,7 +40,7 @@ fun SettingsContent( modifier: Modifier = Modifier, currentLanguage: UiResourceState, seekbarConfig: UiResourceState, - segmentedButtonUiModel: UiResourceState>, + segmentedButtonUiModel: UiResourceState>>, accessPort: UiResourceState, proximityLockEnabled: UiResourceState, onActionEvent: (SettingsAction) -> Unit, From dae9821eba54a77f3ae4b231660d40f8c9a5f538 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 22:19:02 +0100 Subject: [PATCH 075/111] update app language delegate --- .../applanguage/AppLanguageDelegate.kt | 4 ++-- .../applanguage/DefaultAppLanguageDelegate.kt | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt index 8d54cb35..aba76750 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt @@ -1,11 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage +import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import java.util.Locale interface AppLanguageDelegate { - val appLanguageFlow: Flow> + val appLanguageFlow: Flow> fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index 0b56db91..a019317a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -3,22 +3,35 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.GetSupportedLanguagesUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.UpdateAppLanguageUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder +import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import java.util.Locale import javax.inject.Inject -class DefaultAppLanguageDelegate @Inject constructor( +internal class DefaultAppLanguageDelegate @Inject constructor( private val observeCurrentAppLanguage: ObserveCurrentAppLanguageUseCase, private val getSupportedLanguages: GetSupportedLanguagesUseCase, - private val updateAppLanguage: UpdateAppLanguageUseCase + private val updateAppLanguage: UpdateAppLanguageUseCase, + private val uiBuilder: UiBuilder, ) : AppLanguageDelegate { private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) - override val appLanguageFlow: Flow> = currentLanguageFlow.asStateFlow() + override val appLanguageFlow: Flow> = currentLanguageFlow.map { localeResource -> + uiBuilder.buildLanguageUiModel(localeResource) + } + override fun start(scope: CoroutineScope) { - TODO("Not yet implemented") + observeCurrentAppLanguage().onStart { + currentLanguageFlow.value = UiResourceState.Loading + }.onEach { + currentLanguageFlow.value = UiResourceState.Success(it) + }.launchIn(scope) } } From ee9b075545c9fd3a3fae75c591ded291fde00b93 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 22:19:39 +0100 Subject: [PATCH 076/111] pass dummy data from app language observer --- .../language/ObserveCurrentAppLanguageUseCase.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt index f4e52fad..3eaed6fb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt @@ -1,4 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language -class ObserveCurrentAppLanguageUseCase { +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import java.util.Locale +import javax.inject.Inject + +class ObserveCurrentAppLanguageUseCase @Inject constructor() { + operator fun invoke(): Flow { + return flowOf(Locale.ENGLISH) + } } From 232603b9a4aee7c730516facfc6e68ad71c89276 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 22:20:18 +0100 Subject: [PATCH 077/111] rename parameter for appUiMode --- .../features/settings/presentation/viewmodel/SettingsUiState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index e289689b..9b7daad3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -9,7 +9,7 @@ import de.rwth_aachen.phyphox.utils.UiResourceState data class SettingsUiState( val currentLanguage: UiResourceState = UiResourceState.Loading, val graphSize: UiResourceState = UiResourceState.Loading, - val segmentedButtonUiModel: UiResourceState>> = UiResourceState.Loading, + val appUiMode: UiResourceState>> = UiResourceState.Loading, // val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: UiResourceState = UiResourceState.Loading, val proximityLockEnabled: UiResourceState = UiResourceState.Loading, From 47cf931b258830c0aa6c65b84eb0fc90a7f81f4b Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 22:20:42 +0100 Subject: [PATCH 078/111] update settings viewmodel to build SettingsUiStae --- .../settings/presentation/viewmodel/SettingsViewModel.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 20781e5e..5dadc722 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -52,6 +52,13 @@ internal class SettingsViewModel @Inject constructor( graphSizeDelegate.graphSizeFlow, proximityLockDelegate.proximityLockFlow, ) { accessPort, currentLanguage, uiMode, graphSize, proximityLockEnabled -> + SettingsUiState( + currentLanguage = currentLanguage, + graphSize = graphSize, + appUiMode = uiMode, + accessPort = accessPort, + proximityLockEnabled = proximityLockEnabled, + ) }.stateIn( scope = viewModelScope, From ab8a2ec53e5a7eeac0f67c657f0da76f996b571d Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 22:21:04 +0100 Subject: [PATCH 079/111] expose the Language to display uimodel converter --- .../features/settings/presentation/viewmodel/UiBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index aad459c1..a2825f04 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -26,7 +26,7 @@ internal class UiBuilder @Inject constructor() { //region - App Language - private fun buildLanguageUiModel(localeResource: UiResourceState): UiResourceState { + fun buildLanguageUiModel(localeResource: UiResourceState): UiResourceState { return when (localeResource) { UiResourceState.Loading -> UiResourceState.Loading is UiResourceState.Success -> UiResourceState.Success( From 728965f81e05da329f54d1995446e9b2dae84b7b Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:08:55 +0100 Subject: [PATCH 080/111] wire the delagates and usecases --- app/build.gradle.kts | 3 -- .../de/rwth_aachen/phyphox/di/AppModule.kt | 3 +- .../features/settings/di/SettingsModule.kt | 47 +++++++++++++++++++ .../accessport/GetAccessPortRangeUseCase.kt | 4 +- .../ObserveCurrentAccessPortUseCase.kt | 9 +++- .../accessport/UpdateAccessPortUseCase.kt | 4 +- .../graphsize/GetGraphSizeRangeUseCase.kt | 25 ++-------- .../graphsize/GetGraphSizeRangesUseCase.kt | 27 +++++++++++ .../ObserveCurrentGraphConfigUseCase.kt | 21 +++++++++ .../ObserveCurrentGraphSizeUseCase.kt | 13 ++--- .../graphsize/UpdateGraphSizeUseCase.kt | 4 +- .../language/GetSupportedLanguagesUseCase.kt | 4 +- .../language/UpdateAppLanguageUseCase.kt | 4 +- .../uimode/GetSupportedAppUiModeUseCase.kt | 10 +++- .../uimode/ObserveCurrentAppUiModeUseCase.kt | 10 +++- .../uimode/UpdateCurrentAppUiModeUseCase.kt | 4 +- .../presentation/compose/SettingsRoot.kt | 2 +- .../settingscontent/SettingsContent.kt | 6 +-- .../SwitchPreferenceItem.kt | 4 +- .../SwitchPreferenceItemPreviewProvider.kt | 2 +- .../viewmodel/SettingsViewModel.kt | 1 + .../presentation/viewmodel/UiBuilder.kt | 44 ++++++++++++----- .../accessport/AccessPortDelegate.kt | 3 +- .../accessport/DefaultAccessPortDelegate.kt | 22 +++++++-- .../appuimode/DefaultAppUiModeDelegate.kt | 25 +++++++++- .../graphsize/DefaultGraphSizeDelegate.kt | 43 +++++++++++------ .../delegates/graphsize/GraphSizeDelegate.kt | 2 +- 27 files changed, 261 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangesUseCase.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f4595e34..ae1931af 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -131,9 +131,6 @@ android { "UnrememberedMutableState", ) } - kotlinOptions { - freeCompilerArgs = listOf("-XXLanguage:+PropertyParamAnnotationDefaultTargetMode") - } ktlint { android.set(true) ignoreFailures.set(true) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/di/AppModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/di/AppModule.kt index bda88bd7..c782855b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/di/AppModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/di/AppModule.kt @@ -3,8 +3,9 @@ package de.rwth_aachen.phyphox.di import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import de.rwth_aachen.phyphox.features.settings.di.SettingsModule -@Module(includes = []) +@Module(includes = [SettingsModule::class]) @InstallIn(SingletonComponent::class) abstract class AppModule {} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt new file mode 100644 index 00000000..aed99891 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -0,0 +1,47 @@ +package de.rwth_aachen.phyphox.features.settings.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.scopes.ViewModelScoped +import dagger.hilt.components.SingletonComponent +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.DefaultAccessPortDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.DefaultAppLanguageDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.DefaultAppUiModeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.DefaultGraphSizeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.DefaultProximityLockDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate + +@Module +@InstallIn(SingletonComponent::class) +abstract class SettingsModule { + + @Binds + internal abstract fun bindsAccessPortDelegate( + implementation: DefaultAccessPortDelegate, + ): AccessPortDelegate + + @Binds + internal abstract fun bindsAppLanguageDelegate( + implementation: DefaultAppLanguageDelegate, + ): AppLanguageDelegate + + @Binds + internal abstract fun bindsAppUiModeDelegate( + implementation: DefaultAppUiModeDelegate, + ): AppUiModeDelegate + + @Binds + internal abstract fun bindsGraphSizeDelegate( + implementation: DefaultGraphSizeDelegate, + ): GraphSizeDelegate + + @Binds + internal abstract fun bindsProximityLockDelegate( + implementation: DefaultProximityLockDelegate, + ): ProximityLockDelegate +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt index 430ba35c..941382b0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport -class GetAccessPortRangeUseCase { +import javax.inject.Inject + +class GetAccessPortRangeUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt index fa9f29f0..a11cb6c7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt @@ -1,4 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport -class ObserveCurrentAccessPortUseCase { +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +class ObserveCurrentAccessPortUseCase @Inject constructor() { + operator fun invoke(): Flow { + return flowOf(8080) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index 83696818..51826add 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport -class UpdateAccessPortUseCase { +import javax.inject.Inject + +class UpdateAccessPortUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt index 05b0ba9c..66df7a2a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangeUseCase.kt @@ -1,27 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -import de.rwth_aachen.phyphox.features.settings.domain.model.GraphItemsRange -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import javax.inject.Inject -class GetGraphSizeRangeUseCase @Inject constructor( - private val getLabelSizeMultiplierRange: GetLabelSizeMultiplierRangeDomainService, - private val getTextSizeMultiplierRange: GetTextSizeMultiplierRangeDomainService, - private val getLineWidthMultiplierRange: GetLineWidthMultiplierRangeDomainService, - private val getBorderWidthMultiplierRange: GetBorderWidthMultiplierRangeDomainService, -) { - suspend operator fun invoke(): GraphItemsRange = coroutineScope { - val labelSizeRangeDeferred = async { getLabelSizeMultiplierRange() } - val textSizeRangeDeferred = async { getTextSizeMultiplierRange() } - val lineWidthRangeDeferred = async { getLineWidthMultiplierRange() } - val borderWidthRangeDeferred = async { getBorderWidthMultiplierRange() } - - GraphItemsRange( - labelSizeRange = labelSizeRangeDeferred.await(), - textSizeRange = textSizeRangeDeferred.await(), - lineWidthRange = lineWidthRangeDeferred.await(), - borderWidthRange = borderWidthRangeDeferred.await(), - ) +@Deprecated("Migrating to multiplier based graph config instead.") +class GetGraphSizeRangeUseCase @Inject constructor() { + suspend operator fun invoke(): ClosedFloatingPointRange { + return 0f..3f } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangesUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangesUseCase.kt new file mode 100644 index 00000000..1e1cfb3c --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/GetGraphSizeRangesUseCase.kt @@ -0,0 +1,27 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import de.rwth_aachen.phyphox.features.settings.domain.model.GraphItemsRange +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import javax.inject.Inject + +class GetGraphSizeRangesUseCase @Inject constructor( + private val getLabelSizeMultiplierRange: GetLabelSizeMultiplierRangeDomainService, + private val getTextSizeMultiplierRange: GetTextSizeMultiplierRangeDomainService, + private val getLineWidthMultiplierRange: GetLineWidthMultiplierRangeDomainService, + private val getBorderWidthMultiplierRange: GetBorderWidthMultiplierRangeDomainService, +) { + suspend operator fun invoke(): GraphItemsRange = coroutineScope { + val labelSizeRangeDeferred = async { getLabelSizeMultiplierRange() } + val textSizeRangeDeferred = async { getTextSizeMultiplierRange() } + val lineWidthRangeDeferred = async { getLineWidthMultiplierRange() } + val borderWidthRangeDeferred = async { getBorderWidthMultiplierRange() } + + GraphItemsRange( + labelSizeRange = labelSizeRangeDeferred.await(), + textSizeRange = textSizeRangeDeferred.await(), + lineWidthRange = lineWidthRangeDeferred.await(), + borderWidthRange = borderWidthRangeDeferred.await(), + ) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt new file mode 100644 index 00000000..ed08257f --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt @@ -0,0 +1,21 @@ +package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize + +import de.rwth_aachen.phyphox.features.settings.domain.model.GraphConfig +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +class ObserveCurrentGraphConfigUseCase @Inject constructor( + +) { + operator fun invoke(): Flow { + return flowOf( + GraphConfig( + labelSizeMultiplier = 1.0f, + textSizeMultiplier = 1.0f, + lineWidthMultiplier = 1.0f, + borderWidthMultiplier = 1.0f, + ), + ) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt index 043fd3ca..2120f03a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt @@ -1,21 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -import de.rwth_aachen.phyphox.features.settings.domain.model.GraphConfig import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject +@Deprecated("Migrating to multiplier based graph config instead.") class ObserveCurrentGraphSizeUseCase @Inject constructor( ) { - operator fun invoke(): Flow { - return flowOf( - GraphConfig( - labelSizeMultiplier = 1.0f, - textSizeMultiplier = 1.0f, - lineWidthMultiplier = 1.0f, - borderWidthMultiplier = 1.0f, - ), - ) + operator fun invoke(): Flow { + return flowOf(0f) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt index ec476748..0142aec6 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize -class UpdateGraphSizeUseCase { +import javax.inject.Inject + +class UpdateGraphSizeUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt index 4f332d72..7960a2be 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language -class GetSupportedLanguagesUseCase { +import javax.inject.Inject + +class GetSupportedLanguagesUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt index 9f949994..7e7c1c9c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language -class UpdateAppLanguageUseCase { +import javax.inject.Inject + +class UpdateAppLanguageUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt index f18b2245..af025d77 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt @@ -1,4 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class GetSupportedAppUiModeUseCase { +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import kotlinx.coroutines.delay +import javax.inject.Inject + +class GetSupportedAppUiModeUseCase @Inject constructor() { + suspend operator fun invoke(): List { + delay(2500) + return AppUiMode.entries + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt index d8929724..93f44bc3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt @@ -1,4 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class ObserveCurrentAppUiModeUseCase { +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +class ObserveCurrentAppUiModeUseCase @Inject constructor() { + operator fun invoke(): Flow { + return flowOf(AppUiMode.LIGHT) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt index 041dd3b9..067961bc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt @@ -1,4 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -class UpdateCurrentAppUiModeUseCase { +import javax.inject.Inject + +class UpdateCurrentAppUiModeUseCase @Inject constructor(){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 4dbe5317..ac3eadb3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -50,7 +50,7 @@ fun SettingsRoot( modifier = modifier.padding(innerPadding), currentLanguage = uiState.currentLanguage, seekbarConfig = uiState.graphSize, - segmentedButtonUiModel = uiState.segmentedButtonUiModel, + appUiMode = uiState.appUiMode, accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, onActionEvent = onActionEvent, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 4ca0fa0f..3eccea53 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -40,7 +40,7 @@ fun SettingsContent( modifier: Modifier = Modifier, currentLanguage: UiResourceState, seekbarConfig: UiResourceState, - segmentedButtonUiModel: UiResourceState>>, + appUiMode: UiResourceState>>, accessPort: UiResourceState, proximityLockEnabled: UiResourceState, onActionEvent: (SettingsAction) -> Unit, @@ -114,7 +114,7 @@ fun SettingsContent( item { SegmentedButtonPreferenceItem( title = ResourceStringUIModel(resId = R.string.settings_theme_title), - config = segmentedButtonUiModel, + config = appUiMode, iconRes = R.drawable.ic_dark_mode, onOptionSelected = { onActionEvent(SettingsAction.OnUiModeItemSelected(it)) @@ -171,7 +171,7 @@ fun SettingsContentPreview() { SettingsContent( currentLanguage = UiResourceState.Loading, seekbarConfig = UiResourceState.Loading, - segmentedButtonUiModel = UiResourceState.Loading, + appUiMode = UiResourceState.Loading, accessPort = UiResourceState.Loading, proximityLockEnabled = UiResourceState.Loading, onActionEvent = {}, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index d55649d0..7e674f14 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -16,12 +16,12 @@ import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.isChecked import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import de.rwth_aachen.phyphox.utils.UiResourceState +import de.rwth_aachen.phyphox.utils.isChecked @Composable fun SwitchPreferenceItem( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt index 998d19cb..b4a61f2e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt @@ -1,7 +1,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiResourceState +import de.rwth_aachen.phyphox.utils.UiResourceState class SwitchPreferenceItemPreviewProvider : PreviewParameterProvider> { override val values: Sequence> diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 5dadc722..c2ccb7cd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -43,6 +43,7 @@ internal class SettingsViewModel @Inject constructor( appLanguageDelegate.start(scope) appUiModeDelegate.start(scope) graphSizeDelegate.start(scope) + proximityLockDelegate.start(scope) } val uiState = combine( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index a2825f04..779b5732 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -13,18 +13,6 @@ import javax.inject.Inject internal class UiBuilder @Inject constructor() { - fun buildSeekBarConfig( - currentSize: Float, - range: ClosedFloatingPointRange, - ): SeekBarConfig { - return SeekBarConfig( - currentSize = currentSize, - minSize = range.start, - maxSize = range.endInclusive, - ) - } - - //region - App Language fun buildLanguageUiModel(localeResource: UiResourceState): UiResourceState { return when (localeResource) { @@ -67,5 +55,37 @@ internal class UiBuilder @Inject constructor() { AppUiMode.DARK -> ResourceStringUIModel(R.string.settings_mode_dark) } } + + //endregion + + //region - AccessPort + fun buildAccessPortUiModel(portResource: UiResourceState): UiResourceState { + return when (portResource) { + UiResourceState.Loading -> UiResourceState.Loading + is UiResourceState.Success -> UiResourceState.Success( + portResource.data.toString().toStringUIModel(), + ) + } + } + //endregion + + //region - Graph Size + fun buildGraphSizeUiModel( + currentGraphSizeFlow: UiResourceState, + graphSizeRangeFlow: UiResourceState>, + ): UiResourceState { + return when { + currentGraphSizeFlow is UiResourceState.Success && graphSizeRangeFlow is UiResourceState.Success -> { + UiResourceState.Success( + SeekBarConfig( + currentSize = currentGraphSizeFlow.data, + range = graphSizeRangeFlow.data.start..graphSizeRangeFlow.data.endInclusive, + ), + ) + } + + else -> UiResourceState.Loading + } + } //endregion } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index 1a2c93af..b6dbd8e7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -1,11 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport +import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface AccessPortDelegate { - val accessPortFlow: Flow> + val accessPortFlow: Flow> fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index c88efc1c..0114f315 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -3,21 +3,35 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder +import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import javax.inject.Inject -class DefaultAccessPortDelegate @Inject constructor( +internal class DefaultAccessPortDelegate @Inject constructor( private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, private val getAccessPortRange: GetAccessPortRangeUseCase, - private val updateAccessPort: UpdateAccessPortUseCase + private val updateAccessPort: UpdateAccessPortUseCase, + private val uiBuilder: UiBuilder, ) : AccessPortDelegate { private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) - override val accessPortFlow: Flow> = currentAccessPortStateFlow + override val accessPortFlow: Flow> = currentAccessPortStateFlow.map { portResource -> + uiBuilder.buildAccessPortUiModel(portResource) + } + override fun start(scope: CoroutineScope) { - TODO("Not yet implemented") + observeCurrentAccessPort().onStart { + currentAccessPortStateFlow.value = UiResourceState.Loading + }.onEach { + currentAccessPortStateFlow.value = UiResourceState.Success(it) + }.launchIn(scope) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt index 7c54139c..492ad590 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt @@ -11,13 +11,17 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch import javax.inject.Inject internal class DefaultAppUiModeDelegate @Inject constructor( private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, private val getSupportedUiModes: GetSupportedAppUiModeUseCase, private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase, - private val uiBuilder: UiBuilder + private val uiBuilder: UiBuilder, ) : AppUiModeDelegate { private val currentUiModeUiModelFlow = MutableStateFlow>(UiResourceState.Loading) private val supportedUiModesFlowUiModel = @@ -29,7 +33,24 @@ internal class DefaultAppUiModeDelegate @Inject constructor( } override fun start(scope: CoroutineScope) { - TODO("Not yet implemented") + observeAppUiMode(scope) + fetchSupportedUiModes(scope) + } + + private fun fetchSupportedUiModes(scope: CoroutineScope) { + scope.launch { + supportedUiModesFlowUiModel.value = UiResourceState.Success( + getSupportedUiModes() + ) + } + } + + private fun observeAppUiMode(scope: CoroutineScope){ + observeCurrentUiMode().onStart { + currentUiModeUiModelFlow.value = UiResourceState.Loading + }.onEach { + currentUiModeUiModelFlow.value = UiResourceState.Success(it) + }.launchIn(scope) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt index 9bb661d5..25bdc937 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt @@ -4,17 +4,23 @@ import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.GetGrap import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.ObserveCurrentGraphSizeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.UpdateGraphSizeUseCase import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch import javax.inject.Inject -class DefaultGraphSizeDelegate @Inject constructor( +internal class DefaultGraphSizeDelegate @Inject constructor( private val observeCurrentGraphSize: ObserveCurrentGraphSizeUseCase, private val getGraphSizeRange: GetGraphSizeRangeUseCase, private val updateGraphSize: UpdateGraphSizeUseCase, + private val uiBuilder: UiBuilder, ) : GraphSizeDelegate { private val currentGraphSizeFlow = MutableStateFlow>( UiResourceState.Loading, @@ -28,23 +34,32 @@ class DefaultGraphSizeDelegate @Inject constructor( currentGraphSizeFlow, graphSizeRangeFlow, ) { graphSize, range -> - if (graphSize is UiResourceState.Success && range is UiResourceState.Success) { - UiResourceState.Success( - SeekBarConfig( - currentSize = graphSize.data, - range = range.data, - ), - ) - } else { - UiResourceState.Loading - } + uiBuilder.buildGraphSizeUiModel(graphSize, range) } override fun start(scope: CoroutineScope) { - TODO("Not yet implemented") + observeCurrentGraphConfig(scope) + scope.launch { + fetchGraphSizeRange() + } } - override suspend fun updateGraphSize(size: Float) { - TODO("Not yet implemented") + private fun observeCurrentGraphConfig(scope: CoroutineScope) { + observeCurrentGraphSize().onStart { + currentGraphSizeFlow.value = UiResourceState.Loading + }.onEach { + currentGraphSizeFlow.value = UiResourceState.Success(it) + }.launchIn(scope) + } + + private suspend fun fetchGraphSizeRange() { + graphSizeRangeFlow.value = UiResourceState.Success( + getGraphSizeRange(), + ) + } + + + override suspend fun updateGraphSize(size: Int) { + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt index cc947289..37c43de5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt @@ -8,5 +8,5 @@ import kotlinx.coroutines.flow.Flow interface GraphSizeDelegate { val graphSizeFlow: Flow> fun start(scope: CoroutineScope) - suspend fun updateGraphSize(size: Float) + suspend fun updateGraphSize(size: Int) } From 99332061197bf85b4668a1b643055941ff5724b4 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:27:01 +0100 Subject: [PATCH 081/111] declare repository for all usecases --- .../features/settings/di/SettingsModule.kt | 17 ++++++++++++++++- .../domain/data/AppPreferencesRepository.kt | 4 ++++ .../accessport/GetAccessPortRangeUseCase.kt | 10 +++++++++- .../ObserveCurrentAccessPortUseCase.kt | 15 ++++++++++++--- .../accessport/UpdateAccessPortUseCase.kt | 12 +++++++++++- .../ObserveCurrentGraphConfigUseCase.kt | 3 ++- .../graphsize/ObserveCurrentGraphSizeUseCase.kt | 3 ++- .../usecase/graphsize/UpdateGraphSizeUseCase.kt | 5 ++++- .../language/GetSupportedLanguagesUseCase.kt | 5 ++++- .../ObserveCurrentAppLanguageUseCase.kt | 5 ++++- .../language/UpdateAppLanguageUseCase.kt | 5 ++++- ...serveIsCurrentProximityLockEnabledUseCase.kt | 5 ++++- .../UpdateProximityLockStatusUseCase.kt | 5 ++++- .../uimode/GetSupportedAppUiModeUseCase.kt | 5 ++++- .../uimode/ObserveCurrentAppUiModeUseCase.kt | 5 ++++- .../uimode/UpdateCurrentAppUiModeUseCase.kt | 5 ++++- 16 files changed, 92 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt index aed99891..b7a6246d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -3,8 +3,11 @@ package de.rwth_aachen.phyphox.features.settings.di import dagger.Binds import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.android.scopes.ViewModelScoped import dagger.hilt.components.SingletonComponent +import de.rwth_aachen.phyphox.features.settings.data.DefaultAppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.data.local.DefaultLocalAppPreferencesDataSource +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.DefaultAccessPortDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate @@ -44,4 +47,16 @@ abstract class SettingsModule { internal abstract fun bindsProximityLockDelegate( implementation: DefaultProximityLockDelegate, ): ProximityLockDelegate + + @Binds + internal abstract fun bindLocalDataSource( + implementation: DefaultLocalAppPreferencesDataSource, + ): LocalAppPreferencesDataSource + + @Binds + internal abstract fun bndAppPreferencesRepository( + implementation: DefaultAppPreferencesRepository, + ): AppPreferencesRepository + + } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt index 74185a77..4a69ce94 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt @@ -1,4 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.domain.data +import kotlinx.coroutines.flow.Flow + interface AppPreferencesRepository { + fun observeCurrentAccessPort(): Flow + suspend fun updateAccessPort(port: Int): Result } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt index 941382b0..88f9250e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt @@ -2,5 +2,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport import javax.inject.Inject -class GetAccessPortRangeUseCase @Inject constructor(){ +class GetAccessPortRangeUseCase @Inject constructor() { + operator fun invoke(): IntRange { + return MIN_PORT..MAX_PORT + } + + companion object { + const val MIN_PORT = 1024 + const val MAX_PORT = 65536 + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt index a11cb6c7..ae9d82bc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/ObserveCurrentAccessPortUseCase.kt @@ -1,11 +1,20 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import javax.inject.Inject -class ObserveCurrentAccessPortUseCase @Inject constructor() { +class ObserveCurrentAccessPortUseCase @Inject constructor( + private val repository: AppPreferencesRepository, +) { operator fun invoke(): Flow { - return flowOf(8080) + return repository.observeCurrentAccessPort().map { port -> + port ?: DEFAULT_ACCESS_PORT + } + } + + companion object { + const val DEFAULT_ACCESS_PORT = 8080 } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index 51826add..9b08ae8b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -1,6 +1,16 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class UpdateAccessPortUseCase @Inject constructor(){ +class UpdateAccessPortUseCase @Inject constructor( + private val repository: AppPreferencesRepository, +) { + suspend operator fun invoke(port: Int): Result { + return try { + repository.updateAccessPort(port) + } catch (error: Throwable) { + Result.failure(error) + } + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt index ed08257f..d7f0c7c7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphConfigUseCase.kt @@ -1,12 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.model.GraphConfig import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject class ObserveCurrentGraphConfigUseCase @Inject constructor( - + private val repository: AppPreferencesRepository ) { operator fun invoke(): Flow { return flowOf( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt index 2120f03a..d0a2229b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/ObserveCurrentGraphSizeUseCase.kt @@ -1,12 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject @Deprecated("Migrating to multiplier based graph config instead.") class ObserveCurrentGraphSizeUseCase @Inject constructor( - + private val repository: AppPreferencesRepository ) { operator fun invoke(): Flow { return flowOf(0f) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt index 0142aec6..d2684c91 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/graphsize/UpdateGraphSizeUseCase.kt @@ -1,6 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class UpdateGraphSizeUseCase @Inject constructor(){ +class UpdateGraphSizeUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt index 7960a2be..479fe8e7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt @@ -1,6 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class GetSupportedLanguagesUseCase @Inject constructor(){ +class GetSupportedLanguagesUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt index 3eaed6fb..b173c5d3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt @@ -1,11 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import java.util.Locale import javax.inject.Inject -class ObserveCurrentAppLanguageUseCase @Inject constructor() { +class ObserveCurrentAppLanguageUseCase @Inject constructor( + private val repository: AppPreferencesRepository +) { operator fun invoke(): Flow { return flowOf(Locale.ENGLISH) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt index 7e7c1c9c..26460eb2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt @@ -1,6 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class UpdateAppLanguageUseCase @Inject constructor(){ +class UpdateAppLanguageUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt index 11fa1f9b..a6df5707 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/ObserveIsCurrentProximityLockEnabledUseCase.kt @@ -1,10 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject -class ObserveIsCurrentProximityLockEnabledUseCase @Inject constructor(){ +class ObserveIsCurrentProximityLockEnabledUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ operator fun invoke(): Flow { return flowOf(true) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt index 3143a215..c313585a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt @@ -1,8 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.proximitylock +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class UpdateProximityLockStatusUseCase @Inject constructor(){ +class UpdateProximityLockStatusUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ suspend operator fun invoke(enabled: Boolean) : Result{ return Result.success(Unit) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt index af025d77..0de4f170 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt @@ -1,10 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.delay import javax.inject.Inject -class GetSupportedAppUiModeUseCase @Inject constructor() { +class GetSupportedAppUiModeUseCase @Inject constructor( + private val repository: AppPreferencesRepository +) { suspend operator fun invoke(): List { delay(2500) return AppUiMode.entries diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt index 93f44bc3..ac5da30b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt @@ -1,11 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject -class ObserveCurrentAppUiModeUseCase @Inject constructor() { +class ObserveCurrentAppUiModeUseCase @Inject constructor( + private val repository: AppPreferencesRepository +) { operator fun invoke(): Flow { return flowOf(AppUiMode.LIGHT) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt index 067961bc..9b6917c1 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt @@ -1,6 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode +import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import javax.inject.Inject -class UpdateCurrentAppUiModeUseCase @Inject constructor(){ +class UpdateCurrentAppUiModeUseCase @Inject constructor( + private val repository: AppPreferencesRepository +){ } From 73e5506fba5a007cc5bccb2d383c1b622d261796 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:31:33 +0100 Subject: [PATCH 082/111] declare data source and repository functions --- .../settings/data/DefaultAppPreferencesRepository.kt | 7 +++++++ .../settings/domain/data/AppPreferencesRepository.kt | 2 +- .../data/local/LocalAccessPortPreferencesDataSource.kt | 7 ++++++- .../domain/data/local/LocalAppPreferencesDataSource.kt | 6 +++++- .../domain/usecase/accessport/UpdateAccessPortUseCase.kt | 1 + 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt index 0d15e594..dca42365 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt @@ -2,11 +2,18 @@ package de.rwth_aachen.phyphox.features.settings.data import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class DefaultAppPreferencesRepository @Inject constructor( private val localAppPreferencesDataSource: LocalAppPreferencesDataSource ) : AppPreferencesRepository{ + override fun observeCurrentAccessPort(): Flow { + return localAppPreferencesDataSource.observeCurrentAccessPort() + } + override suspend fun updateAccessPort(port: Int) { + return localAppPreferencesDataSource.updateAccessPort(port) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt index 4a69ce94..2de5fd61 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt @@ -4,5 +4,5 @@ import kotlinx.coroutines.flow.Flow interface AppPreferencesRepository { fun observeCurrentAccessPort(): Flow - suspend fun updateAccessPort(port: Int): Result + suspend fun updateAccessPort(port: Int) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt index aa5bb998..78c7d391 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAccessPortPreferencesDataSource.kt @@ -1,3 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -interface LocalAccessPortPreferencesDataSource {} +import kotlinx.coroutines.flow.Flow + +interface LocalAccessPortPreferencesDataSource { + fun observeCurrentAccessPort(): Flow + suspend fun updateAccessPort(port: Int) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt index 14e67b1a..91b7ce56 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt @@ -1,9 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local +import kotlinx.coroutines.flow.Flow + interface LocalAppPreferencesDataSource : LocalAccessPortPreferencesDataSource, LocalGraphSizePreferencesDataSource, LocalLanguagePreferencesDataSource, LocalProximityLockPreferencesDataSource, - LocalUiConfigPreferencesDataSource + LocalUiConfigPreferencesDataSource { + +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index 9b08ae8b..9748ba78 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -9,6 +9,7 @@ class UpdateAccessPortUseCase @Inject constructor( suspend operator fun invoke(port: Int): Result { return try { repository.updateAccessPort(port) + Result.success(Unit) } catch (error: Throwable) { Result.failure(error) } From d1fde8397c497929512ce545049970df761a7a9c Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:34:39 +0100 Subject: [PATCH 083/111] cleanup --- .../data/local/DefaultLocalAppPreferencesDataSource.kt | 8 ++++++++ .../domain/data/local/LocalAppPreferencesDataSource.kt | 6 +----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt index 9b9651a0..bce00c9b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt @@ -1,6 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.data.local import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import kotlinx.coroutines.flow.Flow class DefaultLocalAppPreferencesDataSource: LocalAppPreferencesDataSource { + override fun observeCurrentAccessPort(): Flow { + TODO("Not yet implemented") + } + + override suspend fun updateAccessPort(port: Int) { + TODO("Not yet implemented") + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt index 91b7ce56..14e67b1a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalAppPreferencesDataSource.kt @@ -1,13 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -import kotlinx.coroutines.flow.Flow - interface LocalAppPreferencesDataSource : LocalAccessPortPreferencesDataSource, LocalGraphSizePreferencesDataSource, LocalLanguagePreferencesDataSource, LocalProximityLockPreferencesDataSource, - LocalUiConfigPreferencesDataSource { - -} + LocalUiConfigPreferencesDataSource From 416b54813ba7ed70a112c6564cc27ca1552bba1f Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:49:59 +0100 Subject: [PATCH 084/111] add data layer implementaion --- app/build.gradle.kts | 2 + .../data/DefaultAppPreferencesRepository.kt | 33 +++++++++ .../DefaultLocalAppPreferencesDataSource.kt | 72 +++++++++++++++++-- .../features/settings/di/SettingsModule.kt | 22 +++++- .../domain/data/AppPreferencesRepository.kt | 13 ++++ .../LocalGraphSizePreferencesDataSource.kt | 7 +- .../LocalLanguagePreferencesDataSource.kt | 7 +- ...LocalProximityLockPreferencesDataSource.kt | 7 +- .../LocalUiConfigPreferencesDataSource.kt | 8 ++- gradle/libs.versions.toml | 4 ++ 10 files changed, 166 insertions(+), 9 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ae1931af..342a8a0e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -213,6 +213,8 @@ dependencies { debugImplementation(libs.androidx.compose.ui.test.manifest) detektPlugins(libs.detekt.compose) + implementation(libs.androidx.datastore.preferences) + add("androidTestScreenshotImplementation", libs.junit) add("androidTestScreenshotImplementation", libs.fastlane.screengrab) add("androidTestScreenshotImplementation", libs.androidx.test.rules) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt index dca42365..2f1be89d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt @@ -2,6 +2,7 @@ package de.rwth_aachen.phyphox.features.settings.data import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -16,4 +17,36 @@ class DefaultAppPreferencesRepository @Inject constructor( override suspend fun updateAccessPort(port: Int) { return localAppPreferencesDataSource.updateAccessPort(port) } + + override fun observeGraphSize(): Flow { + return localAppPreferencesDataSource.observeGraphSize() + } + + override suspend fun updateGraphSize(size: Float) { + localAppPreferencesDataSource.updateGraphSize(size) + } + + override fun observeLanguage(): Flow { + return localAppPreferencesDataSource.observeLanguage() + } + + override suspend fun updateLanguage(language: String) { + localAppPreferencesDataSource.updateLanguage(language) + } + + override fun observeProximityLockEnabled(): Flow { + return localAppPreferencesDataSource.observeProximityLockEnabled() + } + + override suspend fun updateProximityLockEnabled(enabled: Boolean) { + localAppPreferencesDataSource.updateProximityLockEnabled(enabled) + } + + override fun observeAppUiMode(): Flow { + return localAppPreferencesDataSource.observeAppUiMode() + } + + override suspend fun updateAppUiMode(mode: AppUiMode) { + localAppPreferencesDataSource.updateAppUiMode(mode) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt index bce00c9b..3891c241 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt @@ -1,14 +1,78 @@ package de.rwth_aachen.phyphox.features.settings.data.local +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.floatPreferencesKey +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import de.rwth_aachen.phyphox.features.settings.di.SettingsDataStore import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject -class DefaultLocalAppPreferencesDataSource: LocalAppPreferencesDataSource { - override fun observeCurrentAccessPort(): Flow { - TODO("Not yet implemented") +class DefaultLocalAppPreferencesDataSource @Inject constructor( + @param:SettingsDataStore private val dataStore: DataStore, +) : LocalAppPreferencesDataSource { + + override fun observeCurrentAccessPort(): Flow = dataStore.data.map { preferences -> + preferences[PreferencesKeys.ACCESS_PORT] } override suspend fun updateAccessPort(port: Int) { - TODO("Not yet implemented") + dataStore.edit { preferences -> + preferences[PreferencesKeys.ACCESS_PORT] = port + } + } + + override fun observeGraphSize(): Flow = dataStore.data.map { preferences -> + preferences[PreferencesKeys.GRAPH_SIZE] + } + + override suspend fun updateGraphSize(size: Float) { + dataStore.edit { preferences -> + preferences[PreferencesKeys.GRAPH_SIZE] = size + } } + + override fun observeLanguage(): Flow = dataStore.data.map { preferences -> + preferences[PreferencesKeys.LANGUAGE] + } + + override suspend fun updateLanguage(language: String) { + dataStore.edit { preferences -> + preferences[PreferencesKeys.LANGUAGE] = language + } + } + + override fun observeProximityLockEnabled(): Flow = dataStore.data.map { preferences -> + preferences[PreferencesKeys.PROXIMITY_LOCK_ENABLED] + } + + override suspend fun updateProximityLockEnabled(enabled: Boolean) { + dataStore.edit { preferences -> + preferences[PreferencesKeys.PROXIMITY_LOCK_ENABLED] = enabled + } + } + + override fun observeAppUiMode(): Flow = dataStore.data.map { preferences -> + preferences[PreferencesKeys.APP_UI_MODE]?.let { AppUiMode.fromString(it) } + } + + override suspend fun updateAppUiMode(mode: AppUiMode) { + dataStore.edit { preferences -> + preferences[PreferencesKeys.APP_UI_MODE] = mode.identifier + } + } + private object PreferencesKeys { + val ACCESS_PORT = intPreferencesKey("access_port") + val GRAPH_SIZE = floatPreferencesKey("graph_size") + val LANGUAGE = stringPreferencesKey("language") + val PROXIMITY_LOCK_ENABLED = booleanPreferencesKey("proximity_lock_enabled") + val APP_UI_MODE = stringPreferencesKey("app_ui_mode") + } + } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt index b7a6246d..27dc205a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -1,8 +1,15 @@ package de.rwth_aachen.phyphox.features.settings.di +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStoreFile import dagger.Binds import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import de.rwth_aachen.phyphox.features.settings.data.DefaultAppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.data.local.DefaultLocalAppPreferencesDataSource @@ -18,6 +25,8 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.DefaultProximityLockDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate +import javax.inject.Qualifier +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) @@ -58,5 +67,16 @@ abstract class SettingsModule { implementation: DefaultAppPreferencesRepository, ): AppPreferencesRepository - + @Provides + @Singleton + @SettingsDataStore + fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore { + return PreferenceDataStoreFactory.create( + produceFile = { context.preferencesDataStoreFile("preferences") } + ) + } } + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class SettingsDataStore diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt index 2de5fd61..50da3db9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt @@ -1,8 +1,21 @@ package de.rwth_aachen.phyphox.features.settings.domain.data +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow interface AppPreferencesRepository { fun observeCurrentAccessPort(): Flow suspend fun updateAccessPort(port: Int) + + fun observeGraphSize(): Flow + suspend fun updateGraphSize(size: Float) + + fun observeLanguage(): Flow + suspend fun updateLanguage(language: String) + + fun observeProximityLockEnabled(): Flow + suspend fun updateProximityLockEnabled(enabled: Boolean) + + fun observeAppUiMode(): Flow + suspend fun updateAppUiMode(mode: AppUiMode) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt index 42cce576..856afabf 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalGraphSizePreferencesDataSource.kt @@ -1,3 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -interface LocalGraphSizePreferencesDataSource +import kotlinx.coroutines.flow.Flow + +interface LocalGraphSizePreferencesDataSource { + fun observeGraphSize(): Flow + suspend fun updateGraphSize(size: Float) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt index bef3af90..b9f3c7d7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt @@ -1,3 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -interface LocalLanguagePreferencesDataSource +import kotlinx.coroutines.flow.Flow + +interface LocalLanguagePreferencesDataSource { + fun observeLanguage(): Flow + suspend fun updateLanguage(language: String) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt index 684e0768..4d3a77d5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalProximityLockPreferencesDataSource.kt @@ -1,3 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -interface LocalProximityLockPreferencesDataSource +import kotlinx.coroutines.flow.Flow + +interface LocalProximityLockPreferencesDataSource { + fun observeProximityLockEnabled(): Flow + suspend fun updateProximityLockEnabled(enabled: Boolean) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt index 9fbc3ace..8e93b43b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalUiConfigPreferencesDataSource.kt @@ -1,3 +1,9 @@ package de.rwth_aachen.phyphox.features.settings.domain.data.local -interface LocalUiConfigPreferencesDataSource +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import kotlinx.coroutines.flow.Flow + +interface LocalUiConfigPreferencesDataSource { + fun observeAppUiMode(): Flow + suspend fun updateAppUiMode(mode: AppUiMode) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2bb743d8..fcc98b2b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,6 +41,7 @@ agp = "31.13.2" ktlint = "14.0.1" detekt = "1.23.8" detektCompose = "0.5.3" +datastore = "1.1.2" [libraries] androidx-multidex = { group = "androidx.multidex", name = "multidex", version.ref = "multidex" } @@ -90,6 +91,9 @@ androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-toolin androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +# DataStore +androidx-datastore-preferences = { group = "androidx.datastore", name = "datastore-preferences", version.ref = "datastore" } + #lint androidx-lint = { module = "com.android.tools.lint:lint", version.ref = "agp" } detekt-compose = { group = "io.nlopez.compose.rules", name = "detekt", version.ref = "detektCompose" } From c60e4a7c2a3a9d2957d3b16bda7b65b658d661b2 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:55:07 +0100 Subject: [PATCH 085/111] brind old settings activity back --- app/src/main/AndroidManifest.xml | 64 ++++++++++++++----- .../ExperimentListActivity.java | 6 +- .../de/rwth_aachen/phyphox/Helper/Helper.java | 2 +- .../SettingsActivity/OldSettingsActivity.java | 38 +++++++++++ .../SettingsFragment.java | 2 +- ...ingsActivity.kt => NewSettingsActivity.kt} | 7 +- 6 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/OldSettingsActivity.java rename app/src/main/java/de/rwth_aachen/phyphox/{features/settings/presentation => SettingsActivity}/SettingsFragment.java (99%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/{SettingsActivity.kt => NewSettingsActivity.kt} (89%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c18a4be8..1a798b37 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - @@ -8,10 +9,18 @@ - - - - + + + + @@ -19,32 +28,39 @@ - - + + - + android:theme="@style/splashTheme"> + + @@ -62,14 +78,18 @@ + + + + @@ -86,15 +106,15 @@ - + + + - + - + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java b/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java index 1037d062..2e089a32 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java +++ b/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java @@ -99,8 +99,8 @@ import de.rwth_aachen.phyphox.PhyphoxFile; import de.rwth_aachen.phyphox.R; import de.rwth_aachen.phyphox.SensorInput; -import de.rwth_aachen.phyphox.features.settings.presentation.SettingsActivity; -import de.rwth_aachen.phyphox.features.settings.presentation.SettingsFragment; +import de.rwth_aachen.phyphox.SettingsActivity.OldSettingsActivity; +import de.rwth_aachen.phyphox.SettingsActivity.SettingsFragment; import de.rwth_aachen.phyphox.camera.depth.DepthInput; import de.rwth_aachen.phyphox.camera.helper.CameraHelper; @@ -305,7 +305,7 @@ else if (item.getItemId() == R.id.action_helpRemote) { return true; } else if (item.getItemId() == R.id.action_settings) { - Intent intent = new Intent(this, SettingsActivity.class); + Intent intent = new Intent(this, OldSettingsActivity.class); startActivity(intent); return true; } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/Helper/Helper.java b/app/src/main/java/de/rwth_aachen/phyphox/Helper/Helper.java index b9a3f8b1..adff69e8 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/Helper/Helper.java +++ b/app/src/main/java/de/rwth_aachen/phyphox/Helper/Helper.java @@ -63,7 +63,7 @@ import de.rwth_aachen.phyphox.InteractiveGraphView; import de.rwth_aachen.phyphox.PlotAreaView; import de.rwth_aachen.phyphox.R; -import de.rwth_aachen.phyphox.features.settings.presentation.SettingsFragment; +import de.rwth_aachen.phyphox.SettingsActivity.SettingsFragment; public abstract class Helper { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/OldSettingsActivity.java b/app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/OldSettingsActivity.java new file mode 100644 index 00000000..082db853 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/OldSettingsActivity.java @@ -0,0 +1,38 @@ +package de.rwth_aachen.phyphox.SettingsActivity; + + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import android.os.Bundle; + + +import de.rwth_aachen.phyphox.Helper.WindowInsetHelper; +import de.rwth_aachen.phyphox.R; + + +public class OldSettingsActivity extends AppCompatActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_settings); + + SettingsFragment settingsFragment = new SettingsFragment(); + getSupportFragmentManager().beginTransaction().replace(R.id.settingsFrame, settingsFragment).commit(); + + Toolbar toolbar = (Toolbar) findViewById(R.id.settingsToolbar); + setSupportActionBar(toolbar); + + ActionBar ab = getSupportActionBar(); + if (ab != null) { + ab.setDisplayHomeAsUpEnabled(true); + ab.setDisplayShowTitleEnabled(true); + } + WindowInsetHelper.setInsets(findViewById(R.id.settingsFrame), WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.IGNORE, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.MARGIN); + WindowInsetHelper.setInsets(findViewById(R.id.settingsToolbar), WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.PADDING, WindowInsetHelper.ApplyTo.IGNORE); + + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java b/app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/SettingsFragment.java similarity index 99% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java rename to app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/SettingsFragment.java index eba49400..486f3c6f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsFragment.java +++ b/app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/SettingsFragment.java @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation; +package de.rwth_aachen.phyphox.SettingsActivity; import android.os.Build; diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt similarity index 89% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index 2a6e4ec4..f3325d0c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/SettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -10,20 +10,15 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.LocalContext -import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint import de.rwth_aachen.phyphox.features.settings.presentation.compose.SettingsRoot import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme -import kotlinx.coroutines.launch @AndroidEntryPoint -class SettingsActivity : ComponentActivity() { +class NewSettingsActivity : ComponentActivity() { private val viewModel: SettingsViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { From 8aeea8e333700895d36031ec047232f88a974ed7 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 3 Jan 2026 23:56:37 +0100 Subject: [PATCH 086/111] fix di stuff --- .../features/settings/di/SettingsModule.kt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt index 27dc205a..d6f87b11 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -67,13 +67,16 @@ abstract class SettingsModule { implementation: DefaultAppPreferencesRepository, ): AppPreferencesRepository - @Provides - @Singleton - @SettingsDataStore - fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore { - return PreferenceDataStoreFactory.create( - produceFile = { context.preferencesDataStoreFile("preferences") } - ) + + companion object { + @Provides + @Singleton + @SettingsDataStore + fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore { + return PreferenceDataStoreFactory.create( + produceFile = { context.preferencesDataStoreFile("preferences") }, + ) + } } } From 37b546132359a0cbfa6b708475eb26d28840487a Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 4 Jan 2026 00:03:55 +0100 Subject: [PATCH 087/111] add way to access the new settings page --- .../phyphox/ExperimentList/ExperimentListActivity.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java b/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java index 2e089a32..93a0935f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java +++ b/app/src/main/java/de/rwth_aachen/phyphox/ExperimentList/ExperimentListActivity.java @@ -83,6 +83,7 @@ import de.rwth_aachen.phyphox.Bluetooth.BluetoothExperimentLoader; import de.rwth_aachen.phyphox.Bluetooth.BluetoothScanDialog; +import de.rwth_aachen.phyphox.BuildConfig; import de.rwth_aachen.phyphox.Experiment; import de.rwth_aachen.phyphox.ExperimentList.datasource.AssetExperimentLoader; import de.rwth_aachen.phyphox.ExperimentList.handler.BluetoothScanner; @@ -103,6 +104,7 @@ import de.rwth_aachen.phyphox.SettingsActivity.SettingsFragment; import de.rwth_aachen.phyphox.camera.depth.DepthInput; import de.rwth_aachen.phyphox.camera.helper.CameraHelper; +import de.rwth_aachen.phyphox.features.settings.presentation.NewSettingsActivity; public class ExperimentListActivity extends AppCompatActivity { @@ -278,6 +280,13 @@ public void onBluetoothScanError(String msg, Boolean isError, Boolean isFatal) { } }); + if(BuildConfig.DEBUG){ + creditsV.setOnLongClickListener(v -> { + startActivity(new Intent(this, NewSettingsActivity.class)); + return true; + }); + } + } private void showPopupMenu(View v) { From c2d1bc0038c939a80c2eb6c3b3230ced5711f79e Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 4 Jan 2026 00:04:46 +0100 Subject: [PATCH 088/111] renaming old settings to settings activity --- app/src/main/AndroidManifest.xml | 2 +- .../phyphox/ExperimentList/ExperimentListActivity.java | 4 ++-- .../{OldSettingsActivity.java => SettingsActivity.java} | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/SettingsActivity/{OldSettingsActivity.java => SettingsActivity.java} (95%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1a798b37..1eb5ffec 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -114,7 +114,7 @@ android:value="de.rwth_aachen.phyphox.ExperimentList.ExperimentListActivity" /> Date: Sun, 4 Jan 2026 00:21:20 +0100 Subject: [PATCH 089/111] map action to events --- .../presentation/NewSettingsActivity.kt | 2 ++ .../presentation/viewmodel/SettingsEvent.kt | 6 ++++-- .../viewmodel/SettingsViewModel.kt | 18 ++++++++++++++++++ .../delegates/accessport/AccessPortDelegate.kt | 2 ++ .../accessport/DefaultAccessPortDelegate.kt | 4 ++++ .../applanguage/AppLanguageDelegate.kt | 2 ++ .../applanguage/DefaultAppLanguageDelegate.kt | 4 ++++ .../delegates/appuimode/AppUiModeDelegate.kt | 2 ++ .../appuimode/DefaultAppUiModeDelegate.kt | 4 ++++ .../graphsize/DefaultGraphSizeDelegate.kt | 8 ++++---- .../delegates/graphsize/GraphSizeDelegate.kt | 2 +- 11 files changed, 47 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index f3325d0c..da1f8478 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -49,6 +49,8 @@ class NewSettingsActivity : ComponentActivity() { SettingsEvent.NavigateBack -> onBackPressedDispatcher?.onBackPressed() SettingsEvent.NavigateToLanguagePicker -> {} is SettingsEvent.OpenWebpage -> {} + is SettingsEvent.OpenWebpageFromResourceID -> {} + SettingsEvent.ShowAccessPortModal -> {} } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt index dd2250f3..5f3cbe33 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt @@ -1,8 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel +import androidx.annotation.StringRes + sealed interface SettingsEvent { data class OpenWebpage(val url: String) : SettingsEvent + data class OpenWebpageFromResourceID(@param:StringRes val urlResourceId: Int) : SettingsEvent data object NavigateBack : SettingsEvent - data object NavigateToLanguagePicker : SettingsEvent - data object ShowAccessPortModal : SettingsAction + data object ShowAccessPortModal : SettingsEvent } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index c2ccb7cd..6e362b96 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -3,6 +3,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate @@ -14,6 +15,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -68,6 +70,22 @@ internal class SettingsViewModel @Inject constructor( ) fun onActionEvent(action: SettingsAction) { + when (action) { + SettingsAction.OnAccessPortClicked -> accessPortDelegate::showAccessPortInputModal + SettingsAction.OnAppLanguageClicked -> appLanguageDelegate::showLanguagePickerModal + SettingsAction.OnBackPressed -> sendEvent(SettingsEvent.NavigateBack) + is SettingsAction.OnGraphSizeChanged -> graphSizeDelegate::updateGraphSize + SettingsAction.OnLearnMoreAboutTranslationClicked -> sendEvent( + SettingsEvent.OpenWebpageFromResourceID( + R.string.settingsTranslation, + ), + ) + is SettingsAction.OnProximityLockChanged -> proximityLockDelegate::updateProximityLockStatus + is SettingsAction.OnUiModeItemSelected -> appUiModeDelegate::updateAppUiMode + } + } + private fun sendEvent(event: SettingsEvent) = viewModelScope.launch { + _uiEvent.emit(event) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index b6dbd8e7..497cd9a2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -8,5 +8,7 @@ import kotlinx.coroutines.flow.Flow interface AccessPortDelegate { val accessPortFlow: Flow> fun start(scope: CoroutineScope) + + suspend fun showAccessPortInputModal() } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 0114f315..2689afc4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -34,5 +34,9 @@ internal class DefaultAccessPortDelegate @Inject constructor( }.launchIn(scope) } + override suspend fun showAccessPortInputModal() { + + } + } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt index aba76750..25e1de7b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt @@ -8,4 +8,6 @@ import kotlinx.coroutines.flow.Flow interface AppLanguageDelegate { val appLanguageFlow: Flow> fun start(scope: CoroutineScope) + + suspend fun showLanguagePickerModal() } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index a019317a..ea34ec73 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -34,4 +34,8 @@ internal class DefaultAppLanguageDelegate @Inject constructor( currentLanguageFlow.value = UiResourceState.Success(it) }.launchIn(scope) } + + override suspend fun showLanguagePickerModal() { + + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt index 5df31c35..2f30f521 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt @@ -9,4 +9,6 @@ import kotlinx.coroutines.flow.Flow interface AppUiModeDelegate { val appUiModeFlow: Flow>>> fun start(scope: CoroutineScope) + + suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt index 492ad590..a1ac12ae 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt @@ -37,6 +37,10 @@ internal class DefaultAppUiModeDelegate @Inject constructor( fetchSupportedUiModes(scope) } + override suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) { + + } + private fun fetchSupportedUiModes(scope: CoroutineScope) { scope.launch { supportedUiModesFlowUiModel.value = UiResourceState.Success( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt index 25bdc937..5ff78a85 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt @@ -44,6 +44,10 @@ internal class DefaultGraphSizeDelegate @Inject constructor( } } + override suspend fun updateGraphSize(size: Float) { + TODO("Not yet implemented") + } + private fun observeCurrentGraphConfig(scope: CoroutineScope) { observeCurrentGraphSize().onStart { currentGraphSizeFlow.value = UiResourceState.Loading @@ -58,8 +62,4 @@ internal class DefaultGraphSizeDelegate @Inject constructor( ) } - - override suspend fun updateGraphSize(size: Int) { - - } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt index 37c43de5..cc947289 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt @@ -8,5 +8,5 @@ import kotlinx.coroutines.flow.Flow interface GraphSizeDelegate { val graphSizeFlow: Flow> fun start(scope: CoroutineScope) - suspend fun updateGraphSize(size: Int) + suspend fun updateGraphSize(size: Float) } From 9ba865b44b2d10782cd085111289f8d303bf4301 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 4 Jan 2026 00:22:06 +0100 Subject: [PATCH 090/111] removed unusable events and actions --- .../features/settings/presentation/NewSettingsActivity.kt | 1 - .../features/settings/presentation/viewmodel/SettingsEvent.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index da1f8478..bb5e6b54 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -47,7 +47,6 @@ class NewSettingsActivity : ComponentActivity() { viewModel.uiEvent.collect { event -> when (event) { SettingsEvent.NavigateBack -> onBackPressedDispatcher?.onBackPressed() - SettingsEvent.NavigateToLanguagePicker -> {} is SettingsEvent.OpenWebpage -> {} is SettingsEvent.OpenWebpageFromResourceID -> {} SettingsEvent.ShowAccessPortModal -> {} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt index 5f3cbe33..f4ef5ba2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt @@ -6,5 +6,4 @@ sealed interface SettingsEvent { data class OpenWebpage(val url: String) : SettingsEvent data class OpenWebpageFromResourceID(@param:StringRes val urlResourceId: Int) : SettingsEvent data object NavigateBack : SettingsEvent - data object ShowAccessPortModal : SettingsEvent } From 3d8c64140894407e8609bd3b082d23d76702c3ac Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 4 Jan 2026 00:22:28 +0100 Subject: [PATCH 091/111] removed unusable events and actions --- .../features/settings/presentation/NewSettingsActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index bb5e6b54..a9dcfd78 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -49,7 +49,6 @@ class NewSettingsActivity : ComponentActivity() { SettingsEvent.NavigateBack -> onBackPressedDispatcher?.onBackPressed() is SettingsEvent.OpenWebpage -> {} is SettingsEvent.OpenWebpageFromResourceID -> {} - SettingsEvent.ShowAccessPortModal -> {} } } } From 2ee793c999896c17dcf4fc87b873f64e15c1923b Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 9 Jan 2026 23:42:36 +0100 Subject: [PATCH 092/111] implement UpdateAccessPortUseCase.kt --- .../model/errors/AccessPortOutOfRange.kt | 3 +++ ...> GetAccessPortRangeApplicationService.kt} | 2 +- .../accessport/UpdateAccessPortUseCase.kt | 25 +++++++++++++++++-- .../accessport/AccessPortDelegate.kt | 4 +++ .../accessport/DefaultAccessPortDelegate.kt | 12 +++++++-- 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/{GetAccessPortRangeUseCase.kt => GetAccessPortRangeApplicationService.kt} (80%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt new file mode 100644 index 00000000..db0fd322 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt @@ -0,0 +1,3 @@ +package de.rwth_aachen.phyphox.features.settings.domain.model.errors + +class AccessPortOutOfRange(message: String? = null) : Throwable(message) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeApplicationService.kt similarity index 80% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeApplicationService.kt index 88f9250e..e03d1708 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/GetAccessPortRangeApplicationService.kt @@ -2,7 +2,7 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport import javax.inject.Inject -class GetAccessPortRangeUseCase @Inject constructor() { +class GetAccessPortRangeApplicationService @Inject constructor() { operator fun invoke(): IntRange { return MIN_PORT..MAX_PORT } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index 9748ba78..7018551c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -1,17 +1,38 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.model.errors.AccessPortOutOfRange import javax.inject.Inject class UpdateAccessPortUseCase @Inject constructor( private val repository: AppPreferencesRepository, + private val getAccessPortRange: GetAccessPortRangeApplicationService, ) { suspend operator fun invoke(port: Int): Result { return try { - repository.updateAccessPort(port) - Result.success(Unit) + if (!isPortWithinRange(port)){ + Result.failure(AccessPortOutOfRange()) + }else{ + repository.updateAccessPort(port) + Result.success(Unit) + } + } catch (error: Throwable) { Result.failure(error) } } + + suspend operator fun invoke(port: String): Result { + return try { + val number = port.toInt() + invoke(number) + } catch (error: NumberFormatException) { + Result.failure(error) + } + } + + private fun isPortWithinRange(port: Int): Boolean { + val validRange = getAccessPortRange() + return validRange.contains(port) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index 497cd9a2..ddfaa422 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -10,5 +10,9 @@ interface AccessPortDelegate { fun start(scope: CoroutineScope) suspend fun showAccessPortInputModal() + + suspend fun setAccessPort(newPort:String) + + suspend fun getAccessPortRange() : IntRange } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 2689afc4..3bedc457 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -1,6 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport -import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeUseCase +import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeApplicationService import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder @@ -17,7 +17,7 @@ import javax.inject.Inject internal class DefaultAccessPortDelegate @Inject constructor( private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, - private val getAccessPortRange: GetAccessPortRangeUseCase, + private val getAccessPortRange: GetAccessPortRangeApplicationService, private val updateAccessPort: UpdateAccessPortUseCase, private val uiBuilder: UiBuilder, ) : AccessPortDelegate { @@ -38,5 +38,13 @@ internal class DefaultAccessPortDelegate @Inject constructor( } + override suspend fun setAccessPort(newPort: String) { + updateAccessPort(newPort) + } + + override suspend fun getAccessPortRange(): IntRange { + TODO("Not yet implemented") + } + } From 332cefea9afba8b18a297c080a3d8a74ecacfecf Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 00:00:11 +0100 Subject: [PATCH 093/111] implement AccessPortDelegate --- .../viewmodel/SettingsSheetUiModel.kt | 2 +- .../presentation/viewmodel/UiBuilder.kt | 20 +++++++--- .../accessport/AccessPortDelegate.kt | 5 ++- .../delegates/accessport/AccessPortUiState.kt | 10 +++++ .../accessport/DefaultAccessPortDelegate.kt | 39 +++++++++++++++---- 5 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt index 41161499..9b4ebfc2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt @@ -5,7 +5,7 @@ import de.rwth_aachen.phyphox.ui.string.StringUIModel sealed interface SettingsSheetUiModel { data class AccessPortSheetUiModel( val currentPort: StringUIModel, - val range: ClosedFloatingPointRange, + val range: IntRange, val error: StringUIModel? = null, ) : SettingsSheetUiModel } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index 779b5732..b0e931c4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -4,6 +4,7 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -59,12 +60,21 @@ internal class UiBuilder @Inject constructor() { //endregion //region - AccessPort - fun buildAccessPortUiModel(portResource: UiResourceState): UiResourceState { - return when (portResource) { - UiResourceState.Loading -> UiResourceState.Loading - is UiResourceState.Success -> UiResourceState.Success( - portResource.data.toString().toStringUIModel(), + fun buildAccessPortUiModel( + portResource: UiResourceState, + range: UiResourceState, + inputModal: SettingsSheetUiModel.AccessPortSheetUiModel?, + ): UiResourceState { + return if (portResource is UiResourceState.Success && range is UiResourceState.Success) { + UiResourceState.Success( + AccessPortUiState( + currentAccessPort = portResource.data.toString().toStringUIModel(), + accessPortRange = range.data, + inputModal = inputModal, + ), ) + } else { + UiResourceState.Loading } } //endregion diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index ddfaa422..8f38cc34 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -6,13 +6,14 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface AccessPortDelegate { - val accessPortFlow: Flow> + val accessPortFlow: Flow> fun start(scope: CoroutineScope) suspend fun showAccessPortInputModal() suspend fun setAccessPort(newPort:String) - suspend fun getAccessPortRange() : IntRange + suspend fun dismissAccessPortInputModal() + } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt new file mode 100644 index 00000000..f2d1e63f --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class AccessPortUiState( + val currentAccessPort: StringUIModel, + val accessPortRange: IntRange, + val inputModal: SettingsSheetUiModel.AccessPortSheetUiModel? = null, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 3bedc457..191e94a5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -3,16 +3,18 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeApplicationService import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder -import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.launch import javax.inject.Inject internal class DefaultAccessPortDelegate @Inject constructor( @@ -22,8 +24,15 @@ internal class DefaultAccessPortDelegate @Inject constructor( private val uiBuilder: UiBuilder, ) : AccessPortDelegate { private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) - override val accessPortFlow: Flow> = currentAccessPortStateFlow.map { portResource -> - uiBuilder.buildAccessPortUiModel(portResource) + private val accessPortRangeStateFlow = MutableStateFlow>(UiResourceState.Loading) + private val inputModalStaFlow = MutableStateFlow(null) + + override val accessPortFlow: Flow> = combine( + currentAccessPortStateFlow, + accessPortRangeStateFlow, + inputModalStaFlow, + ) { accessPort, range, inputModal -> + uiBuilder.buildAccessPortUiModel(accessPort, range, inputModal) } override fun start(scope: CoroutineScope) { @@ -32,19 +41,33 @@ internal class DefaultAccessPortDelegate @Inject constructor( }.onEach { currentAccessPortStateFlow.value = UiResourceState.Success(it) }.launchIn(scope) + scope.launch { + getAccessPortRange().let { + accessPortRangeStateFlow.value = UiResourceState.Success(it) + } + } } override suspend fun showAccessPortInputModal() { - + val current = currentAccessPortStateFlow.value + val range = accessPortRangeStateFlow.value + if ( + current is UiResourceState.Success && + range is UiResourceState.Success + ) { + inputModalStaFlow.value = SettingsSheetUiModel.AccessPortSheetUiModel( + currentPort = current.data.toString().toStringUIModel(), + range = range.data, + ) + } } override suspend fun setAccessPort(newPort: String) { updateAccessPort(newPort) } - override suspend fun getAccessPortRange(): IntRange { - TODO("Not yet implemented") + override suspend fun dismissAccessPortInputModal() { + inputModalStaFlow.value = null } - } From 3ed404404841deb3d2cd58625e4ac3875e88d556 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 00:31:58 +0100 Subject: [PATCH 094/111] simplify accessport delegate --- .../settingscontent/SettingsContent.kt | 1 + .../viewmodel/SettingsSheetUiModel.kt | 8 +---- .../presentation/viewmodel/SettingsUiState.kt | 23 ++++++++++++- .../viewmodel/SettingsViewModel.kt | 11 +++++-- .../presentation/viewmodel/UiBuilder.kt | 13 ++------ .../accessport/AccessPortDelegate.kt | 4 ++- .../accessport/AccessPortSheetUiModel.kt | 10 ++++++ .../delegates/accessport/AccessPortUiState.kt | 1 - .../accessport/DefaultAccessPortDelegate.kt | 32 +++++++------------ 9 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortSheetUiModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index 3eccea53..d2b27ec8 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -29,6 +29,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbu import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt index 9b4ebfc2..bd08cd61 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt @@ -2,10 +2,4 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.ui.string.StringUIModel -sealed interface SettingsSheetUiModel { - data class AccessPortSheetUiModel( - val currentPort: StringUIModel, - val range: IntRange, - val error: StringUIModel? = null, - ) : SettingsSheetUiModel -} +interface SettingsSheetUiModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 9b7daad3..1d949901 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -3,6 +3,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState @@ -10,8 +11,28 @@ data class SettingsUiState( val currentLanguage: UiResourceState = UiResourceState.Loading, val graphSize: UiResourceState = UiResourceState.Loading, val appUiMode: UiResourceState>> = UiResourceState.Loading, -// val dynamicTheme: ResourceState = ResourceState.Loading, val accessPort: UiResourceState = UiResourceState.Loading, val proximityLockEnabled: UiResourceState = UiResourceState.Loading, val modal: SettingsSheetUiModel? = null, +) { + constructor( + userSettings: UserSettings = UserSettings(), + modal: SettingsSheetUiModel? = null, + ) : this( + currentLanguage = userSettings.currentLanguage, + graphSize = userSettings.graphSize, + appUiMode = userSettings.appUiMode, + accessPort = userSettings.accessPort, + proximityLockEnabled = userSettings.proximityLockEnabled, + modal = modal, + ) +} + +data class UserSettings( + val currentLanguage: UiResourceState = UiResourceState.Loading, + val graphSize: UiResourceState = UiResourceState.Loading, + val appUiMode: UiResourceState>> = UiResourceState.Loading, + val accessPort: UiResourceState = UiResourceState.Loading, + val proximityLockEnabled: UiResourceState = UiResourceState.Loading, + // val dynamicTheme: ResourceState = ResourceState.Loading, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index 6e362b96..a151cb91 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -40,6 +40,8 @@ internal class SettingsViewModel @Inject constructor( start(viewModelScope) } + val uiModalFlow = accessPortDelegate.inputModal + override fun start(scope: CoroutineScope) { accessPortDelegate.start(scope) appLanguageDelegate.start(scope) @@ -55,14 +57,18 @@ internal class SettingsViewModel @Inject constructor( graphSizeDelegate.graphSizeFlow, proximityLockDelegate.proximityLockFlow, ) { accessPort, currentLanguage, uiMode, graphSize, proximityLockEnabled -> - SettingsUiState( + UserSettings( currentLanguage = currentLanguage, graphSize = graphSize, appUiMode = uiMode, accessPort = accessPort, proximityLockEnabled = proximityLockEnabled, ) - + }.combine(uiModalFlow) { userSettings, modal -> + SettingsUiState( + userSettings = userSettings, + modal = modal, + ) }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), @@ -80,6 +86,7 @@ internal class SettingsViewModel @Inject constructor( R.string.settingsTranslation, ), ) + is SettingsAction.OnProximityLockChanged -> proximityLockDelegate::updateProximityLockStatus is SettingsAction.OnUiModeItemSelected -> appUiModeDelegate::updateAppUiMode } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index b0e931c4..aa18bb05 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -4,7 +4,6 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -62,16 +61,10 @@ internal class UiBuilder @Inject constructor() { //region - AccessPort fun buildAccessPortUiModel( portResource: UiResourceState, - range: UiResourceState, - inputModal: SettingsSheetUiModel.AccessPortSheetUiModel?, - ): UiResourceState { - return if (portResource is UiResourceState.Success && range is UiResourceState.Success) { + ): UiResourceState { + return if (portResource is UiResourceState.Success) { UiResourceState.Success( - AccessPortUiState( - currentAccessPort = portResource.data.toString().toStringUIModel(), - accessPortRange = range.data, - inputModal = inputModal, - ), + portResource.data.toString().toStringUIModel(), ) } else { UiResourceState.Loading diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index 8f38cc34..f1b34412 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -1,12 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow interface AccessPortDelegate { - val accessPortFlow: Flow> + val accessPortFlow: Flow> + val inputModal: Flow fun start(scope: CoroutineScope) suspend fun showAccessPortInputModal() diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortSheetUiModel.kt new file mode 100644 index 00000000..d91cb6ef --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortSheetUiModel.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class AccessPortSheetUiModel( + val currentPort: StringUIModel, + val range: IntRange, + val error: StringUIModel? = null, +) : SettingsSheetUiModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt index f2d1e63f..4340a980 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortUiState.kt @@ -6,5 +6,4 @@ import de.rwth_aachen.phyphox.ui.string.StringUIModel data class AccessPortUiState( val currentAccessPort: StringUIModel, val accessPortRange: IntRange, - val inputModal: SettingsSheetUiModel.AccessPortSheetUiModel? = null, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 191e94a5..617f61ac 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -3,18 +3,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeApplicationService import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder +import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch import javax.inject.Inject internal class DefaultAccessPortDelegate @Inject constructor( @@ -24,40 +23,31 @@ internal class DefaultAccessPortDelegate @Inject constructor( private val uiBuilder: UiBuilder, ) : AccessPortDelegate { private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) - private val accessPortRangeStateFlow = MutableStateFlow>(UiResourceState.Loading) - private val inputModalStaFlow = MutableStateFlow(null) - override val accessPortFlow: Flow> = combine( - currentAccessPortStateFlow, - accessPortRangeStateFlow, - inputModalStaFlow, - ) { accessPort, range, inputModal -> - uiBuilder.buildAccessPortUiModel(accessPort, range, inputModal) + override val accessPortFlow: Flow> = currentAccessPortStateFlow.map { accessPort -> + uiBuilder.buildAccessPortUiModel(accessPort) } + private val inputModalStaFlow = MutableStateFlow(null) + override val inputModal: Flow = inputModalStaFlow + override fun start(scope: CoroutineScope) { observeCurrentAccessPort().onStart { currentAccessPortStateFlow.value = UiResourceState.Loading }.onEach { currentAccessPortStateFlow.value = UiResourceState.Success(it) }.launchIn(scope) - scope.launch { - getAccessPortRange().let { - accessPortRangeStateFlow.value = UiResourceState.Success(it) - } - } } override suspend fun showAccessPortInputModal() { val current = currentAccessPortStateFlow.value - val range = accessPortRangeStateFlow.value + val range = getAccessPortRange() if ( - current is UiResourceState.Success && - range is UiResourceState.Success + current is UiResourceState.Success ) { - inputModalStaFlow.value = SettingsSheetUiModel.AccessPortSheetUiModel( + inputModalStaFlow.value = AccessPortSheetUiModel( currentPort = current.data.toString().toStringUIModel(), - range = range.data, + range = range, ) } } From 9eab58806fd3063b10f278d4efe773ae76cda2a7 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 00:41:55 +0100 Subject: [PATCH 095/111] implementing modal --- .../compose/AccessPortBottomSheet.kt | 127 ++++++++++++++++++ .../presentation/compose/SettingsRoot.kt | 25 +++- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt new file mode 100644 index 00000000..10cef9f8 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt @@ -0,0 +1,127 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.R // Assuming your string resources are here +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortSheetUiModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.resolve +import de.rwth_aachen.phyphox.ui.string.toStringUIModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AccessPortBottomSheet( + uiModel: AccessPortSheetUiModel, + onDismiss: () -> Unit, + onConfirm: (String) -> Unit, + sheetState: SheetState = rememberModalBottomSheetState(), +) { + val initial = uiModel.currentPort.resolve() + var textState by remember(uiModel.currentPort) { + mutableStateOf(initial) + } + + val isError = uiModel.error != null + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 32.dp) + ) { + // Title for the bottom sheet + Text( + text = "Title", + style = androidx.compose.material3.MaterialTheme.typography.titleLarge + ) + Spacer(modifier = Modifier.height(24.dp)) + + // Text input field + OutlinedTextField( + value = textState, + onValueChange = { textState = it}, + modifier = Modifier.fillMaxWidth(), + label = { Text(text = "Port") }, + isError = isError, + supportingText = { + uiModel.error?.let { + Text(text = it.resolve()) + } + }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + singleLine = true + ) + Spacer(modifier = Modifier.height(16.dp)) + + // Action buttons + TextButton( + onClick = { + if (!isError) { + onConfirm(textState) + } + }, + modifier = Modifier.align(Alignment.End), + enabled = !isError // Disable confirm button if there's an error + ) { + Text(text = stringResource(id = android.R.string.ok)) + } + } + } +} + + +@OptIn(ExperimentalMaterial3Api::class) +@Preview(showBackground = true) +@Composable +private fun AccessPortBottomSheetPreview() { + // State for showing the bottom sheet in the preview + val sheetState = rememberModalBottomSheetState() + + // Example with valid input + val uiModel = AccessPortSheetUiModel( + currentPort = "8080".toStringUIModel(), + range = 1024..65535, + error = "Some error".toStringUIModel() + ) + + // Example with an error state (uncomment to see) + /* + val uiModelWithError = AccessPortSheetUiModel( + currentPort = StringUIModel.StaticString("100"), // Invalid initial value + range = 1024..65535, + error = StringUIModel.StringResource(R.string.settings_we_access_port_invalid, 1024, 65535) + ) + */ + + AccessPortBottomSheet( + uiModel = uiModel, + onDismiss = {}, + onConfirm = {}, + sheetState = sheetState + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index ac3eadb3..ab8d10ae 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -9,7 +9,9 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark @@ -17,7 +19,9 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscontent.SettingsContent import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortSheetUiModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -26,6 +30,8 @@ fun SettingsRoot( uiState: SettingsUiState, onActionEvent: (SettingsAction) -> Unit, ) { + val sheetState = rememberModalBottomSheetState() + val coroutineScope = rememberCoroutineScope() Scaffold( topBar = { TopAppBar( @@ -57,6 +63,23 @@ fun SettingsRoot( ) } + when(val modal = uiState.modal){ + is AccessPortSheetUiModel ->AccessPortBottomSheet( + uiModel = modal, + sheetState = sheetState, + onDismiss = { + coroutineScope.launch { + sheetState.hide() + } + }, + onConfirm = { newPort -> + // viewModel.updatePort(newPort) + coroutineScope.launch { + sheetState.hide() + } + }, + ) + } } @Composable @@ -65,7 +88,7 @@ internal fun SettingsRootPreview() { PhyphoxTheme { SettingsRoot( uiState = SettingsUiState(), - onActionEvent = {} + onActionEvent = {}, ) } } From c59f2f4bf83cfaa5e97a4d36e73339e62450e2d6 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 00:51:10 +0100 Subject: [PATCH 096/111] implementing modal for access port --- .../presentation/compose/SettingsRoot.kt | 3 ++- .../presentation/viewmodel/SettingsAction.kt | 3 +++ .../viewmodel/SettingsViewModel.kt | 18 +++++++++++++++--- .../accessport/DefaultAccessPortDelegate.kt | 3 +++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index ab8d10ae..08a82f6e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -70,10 +70,11 @@ fun SettingsRoot( onDismiss = { coroutineScope.launch { sheetState.hide() + onActionEvent.invoke(SettingsAction.OnModalDismissed) } }, onConfirm = { newPort -> - // viewModel.updatePort(newPort) + onActionEvent.invoke(SettingsAction.OnAccessPortChanged(newPort)) coroutineScope.launch { sheetState.hide() } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt index d347b4bb..e80ec419 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -8,6 +8,9 @@ sealed interface SettingsAction { data class OnUiModeItemSelected(val appUiMode: SegmentedButtonUiModel) : SettingsAction data class OnGraphSizeChanged(val size: Float) : SettingsAction data class OnProximityLockChanged(val enabled: Boolean) : SettingsAction + data class OnAccessPortChanged(val newPort: String) : SettingsAction + data object OnModalDismissed : SettingsAction + data object OnAppLanguageClicked : SettingsAction data object OnLearnMoreAboutTranslationClicked : SettingsAction data object OnAccessPortClicked : SettingsAction diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index a151cb91..eb662a48 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.* import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate @@ -77,18 +78,29 @@ internal class SettingsViewModel @Inject constructor( fun onActionEvent(action: SettingsAction) { when (action) { - SettingsAction.OnAccessPortClicked -> accessPortDelegate::showAccessPortInputModal + SettingsAction.OnAccessPortClicked -> { + viewModelScope.launch { + accessPortDelegate.showAccessPortInputModal() + } + } SettingsAction.OnAppLanguageClicked -> appLanguageDelegate::showLanguagePickerModal - SettingsAction.OnBackPressed -> sendEvent(SettingsEvent.NavigateBack) + SettingsAction.OnBackPressed -> sendEvent(NavigateBack) is SettingsAction.OnGraphSizeChanged -> graphSizeDelegate::updateGraphSize SettingsAction.OnLearnMoreAboutTranslationClicked -> sendEvent( - SettingsEvent.OpenWebpageFromResourceID( + OpenWebpageFromResourceID( R.string.settingsTranslation, ), ) is SettingsAction.OnProximityLockChanged -> proximityLockDelegate::updateProximityLockStatus is SettingsAction.OnUiModeItemSelected -> appUiModeDelegate::updateAppUiMode + is SettingsAction.OnAccessPortChanged -> viewModelScope.launch { + accessPortDelegate.setAccessPort(action.newPort) + } + + SettingsAction.OnModalDismissed -> viewModelScope.launch { + accessPortDelegate.dismissAccessPortInputModal() + } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 617f61ac..86e6e180 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -15,7 +15,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import javax.inject.Inject +import javax.inject.Singleton +@Singleton internal class DefaultAccessPortDelegate @Inject constructor( private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, private val getAccessPortRange: GetAccessPortRangeApplicationService, @@ -54,6 +56,7 @@ internal class DefaultAccessPortDelegate @Inject constructor( override suspend fun setAccessPort(newPort: String) { updateAccessPort(newPort) + inputModalStaFlow.value = null } override suspend fun dismissAccessPortInputModal() { From 4f93ad212b4c2488835e8c20d7dc87a50308204d Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 16:16:39 +0100 Subject: [PATCH 097/111] implement the access port bottom sheet --- .../compose/AccessPortBottomSheet.kt | 65 ++++++++++--------- .../presentation/compose/SettingsRoot.kt | 39 ++++++----- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt index 10cef9f8..839fc0b4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt @@ -6,27 +6,27 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.SheetState import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import de.rwth_aachen.phyphox.R // Assuming your string resources are here +import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortSheetUiModel -import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -38,12 +38,15 @@ fun AccessPortBottomSheet( onConfirm: (String) -> Unit, sheetState: SheetState = rememberModalBottomSheetState(), ) { - val initial = uiModel.currentPort.resolve() - var textState by remember(uiModel.currentPort) { - mutableStateOf(initial) + val context = LocalContext.current + + var port by remember { mutableStateOf(uiModel.currentPort.resolve(context)) } + var isError by remember { mutableStateOf(false) } + LaunchedEffect(uiModel.error) { + isError = uiModel.error != null } - val isError = uiModel.error != null + ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState, @@ -52,44 +55,46 @@ fun AccessPortBottomSheet( modifier = Modifier .fillMaxWidth() .padding(horizontal = 24.dp) - .padding(bottom = 32.dp) + .padding(bottom = 32.dp), ) { - // Title for the bottom sheet Text( - text = "Title", - style = androidx.compose.material3.MaterialTheme.typography.titleLarge + text = stringResource(R.string.settingsPort), + style = androidx.compose.material3.MaterialTheme.typography.titleLarge, ) Spacer(modifier = Modifier.height(24.dp)) - - // Text input field OutlinedTextField( - value = textState, - onValueChange = { textState = it}, + value = port, + onValueChange = { + port = it + isError = false + }, modifier = Modifier.fillMaxWidth(), - label = { Text(text = "Port") }, + label = { + Text( + text = stringResource(R.string.settingsPort), + ) + }, isError = isError, supportingText = { - uiModel.error?.let { - Text(text = it.resolve()) + if (uiModel.error != null && isError) { + Text(text = uiModel.error.resolve()) } }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true + singleLine = true, ) Spacer(modifier = Modifier.height(16.dp)) - // Action buttons - TextButton( + Button( + modifier = Modifier.fillMaxWidth(), onClick = { - if (!isError) { - onConfirm(textState) - } + onConfirm(port) }, - modifier = Modifier.align(Alignment.End), - enabled = !isError // Disable confirm button if there's an error + enabled = !isError, ) { - Text(text = stringResource(id = android.R.string.ok)) + Text(stringResource(id = android.R.string.ok)) } + } } } @@ -106,7 +111,7 @@ private fun AccessPortBottomSheetPreview() { val uiModel = AccessPortSheetUiModel( currentPort = "8080".toStringUIModel(), range = 1024..65535, - error = "Some error".toStringUIModel() + error = "Some error".toStringUIModel(), ) // Example with an error state (uncomment to see) @@ -122,6 +127,6 @@ private fun AccessPortBottomSheetPreview() { uiModel = uiModel, onDismiss = {}, onConfirm = {}, - sheetState = sheetState + sheetState = sheetState, ) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 08a82f6e..7f65acc6 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -32,6 +33,11 @@ fun SettingsRoot( ) { val sheetState = rememberModalBottomSheetState() val coroutineScope = rememberCoroutineScope() + LaunchedEffect(uiState.modal) { + if (uiState.modal == null) { + sheetState.hide() + } + } Scaffold( topBar = { TopAppBar( @@ -61,25 +67,22 @@ fun SettingsRoot( proximityLockEnabled = uiState.proximityLockEnabled, onActionEvent = onActionEvent, - ) + ) } - when(val modal = uiState.modal){ - is AccessPortSheetUiModel ->AccessPortBottomSheet( - uiModel = modal, - sheetState = sheetState, - onDismiss = { - coroutineScope.launch { - sheetState.hide() - onActionEvent.invoke(SettingsAction.OnModalDismissed) - } - }, - onConfirm = { newPort -> - onActionEvent.invoke(SettingsAction.OnAccessPortChanged(newPort)) - coroutineScope.launch { - sheetState.hide() - } - }, - ) + when (val modal = uiState.modal) { + is AccessPortSheetUiModel -> AccessPortBottomSheet( + uiModel = modal, + sheetState = sheetState, + onDismiss = { + coroutineScope.launch { + sheetState.hide() + onActionEvent.invoke(SettingsAction.OnModalDismissed) + } + }, + onConfirm = { newPort -> + onActionEvent.invoke(SettingsAction.OnAccessPortChanged(newPort)) + }, + ) } } From 3ecbbbfbba614d6c2f4cb3d09e6e458497f514f9 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 16:31:35 +0100 Subject: [PATCH 098/111] validatin in bottomsheet --- .../domain/model/errors/AccessPortOutOfRange.kt | 2 +- .../accessport/UpdateAccessPortUseCase.kt | 16 +++++++--------- .../compose/AccessPortBottomSheet.kt | 4 +++- .../settings/presentation/viewmodel/UiBuilder.kt | 8 ++++++++ .../accessport/DefaultAccessPortDelegate.kt | 8 +++++++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt index db0fd322..095b4bba 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/errors/AccessPortOutOfRange.kt @@ -1,3 +1,3 @@ package de.rwth_aachen.phyphox.features.settings.domain.model.errors -class AccessPortOutOfRange(message: String? = null) : Throwable(message) +class AccessPortOutOfRange(val intRange: IntRange) : Throwable(message = null) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt index 7018551c..298efba5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/accessport/UpdateAccessPortUseCase.kt @@ -10,13 +10,9 @@ class UpdateAccessPortUseCase @Inject constructor( ) { suspend operator fun invoke(port: Int): Result { return try { - if (!isPortWithinRange(port)){ - Result.failure(AccessPortOutOfRange()) - }else{ - repository.updateAccessPort(port) - Result.success(Unit) - } - + validatePort(port) + repository.updateAccessPort(port) + Result.success(Unit) } catch (error: Throwable) { Result.failure(error) } @@ -31,8 +27,10 @@ class UpdateAccessPortUseCase @Inject constructor( } } - private fun isPortWithinRange(port: Int): Boolean { + private fun validatePort(port: Int) { val validRange = getAccessPortRange() - return validRange.contains(port) + if (!validRange.contains(port)) { + throw AccessPortOutOfRange(validRange) + } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt index 839fc0b4..090370ba 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AccessPortBottomSheet.kt @@ -66,7 +66,7 @@ fun AccessPortBottomSheet( value = port, onValueChange = { port = it - isError = false + isError = !uiModel.range.contains(it.toIntOrNull()) }, modifier = Modifier.fillMaxWidth(), label = { @@ -78,6 +78,8 @@ fun AccessPortBottomSheet( supportingText = { if (uiModel.error != null && isError) { Text(text = uiModel.error.resolve()) + }else{ + Text(text = uiModel.range.toString()) } }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index aa18bb05..727a69a7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -2,6 +2,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.domain.model.errors.AccessPortOutOfRange import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel @@ -90,5 +91,12 @@ internal class UiBuilder @Inject constructor() { else -> UiResourceState.Loading } } + + fun getErrorMessage(error: Throwable): StringUIModel { + return when (error) { + is AccessPortOutOfRange -> "${error.intRange.first} - ${error.intRange.last}".toStringUIModel() + else -> "Unknown error".toStringUIModel() + } + } //endregion } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index 86e6e180..b4ff9960 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -56,7 +56,13 @@ internal class DefaultAccessPortDelegate @Inject constructor( override suspend fun setAccessPort(newPort: String) { updateAccessPort(newPort) - inputModalStaFlow.value = null + .onSuccess { + inputModalStaFlow.value = null + }.onFailure { + inputModalStaFlow.value = inputModalStaFlow.value?.copy( + error = uiBuilder.getErrorMessage(it), + ) + } } override suspend fun dismissAccessPortInputModal() { From 7b9bd11b533c2eea46bd3bd3493901777593cb75 Mon Sep 17 00:00:00 2001 From: owl Date: Sat, 10 Jan 2026 23:30:42 +0100 Subject: [PATCH 099/111] setting up language --- .../data/DefaultAppPreferencesRepository.kt | 24 +++++--- .../DefaultLocalAppPreferencesDataSource.kt | 6 ++ .../settings/data/local/SystemDataSource.kt | 12 ++++ .../domain/data/AppPreferencesRepository.kt | 8 ++- .../LocalLanguagePreferencesDataSource.kt | 1 + .../local/converter/AppLanguageConverter.kt | 10 ++++ .../settings/domain/model/AppLanguage.kt | 10 ++++ .../language/GetSupportedLanguagesUseCase.kt | 9 ++- .../ObserveCurrentAppLanguageUseCase.kt | 10 ++-- .../language/UpdateAppLanguageUseCase.kt | 17 +++++- .../UpdateProximityLockStatusUseCase.kt | 13 +++-- .../compose/AppLanguageBottomSheet.kt | 51 ++++++++++++++++ .../presentation/compose/SettingsRoot.kt | 22 ++++++- .../ClickablePreferenceItem.kt | 35 +++++++++++ .../settingscontent/SettingsContent.kt | 6 +- .../presentation/viewmodel/SettingsAction.kt | 2 +- .../viewmodel/SettingsSheetUiModel.kt | 2 - .../presentation/viewmodel/SettingsUiState.kt | 5 +- .../viewmodel/SettingsViewModel.kt | 58 ++++++++++--------- .../presentation/viewmodel/UiBuilder.kt | 27 +++++++-- .../viewmodel/delegates/SettingsDelegate.kt | 9 +++ .../accessport/AccessPortDelegate.kt | 14 ++--- .../accessport/DefaultAccessPortDelegate.kt | 5 +- .../applanguage/AppLanguageDelegate.kt | 14 +++-- .../applanguage/AppLanguageSheetUiModel.kt | 13 +++++ .../applanguage/DefaultAppLanguageDelegate.kt | 36 ++++++++++-- .../delegates/applanguage/LanguageUiModel.kt | 10 ++++ .../delegates/appuimode/AppUiModeDelegate.kt | 4 +- .../delegates/graphsize/GraphSizeDelegate.kt | 4 +- .../proximitylock/ProximityLockDelegate.kt | 5 +- 30 files changed, 347 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/SystemDataSource.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/converter/AppLanguageConverter.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppLanguage.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageSheetUiModel.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/LanguageUiModel.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt index 2f1be89d..0f8d277f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/DefaultAppPreferencesRepository.kt @@ -2,14 +2,17 @@ package de.rwth_aachen.phyphox.features.settings.data import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource +import de.rwth_aachen.phyphox.features.settings.domain.data.local.converter.toAppLanguage +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import javax.inject.Inject class DefaultAppPreferencesRepository @Inject constructor( - private val localAppPreferencesDataSource: LocalAppPreferencesDataSource -) : AppPreferencesRepository{ + private val localAppPreferencesDataSource: LocalAppPreferencesDataSource, +) : AppPreferencesRepository { override fun observeCurrentAccessPort(): Flow { return localAppPreferencesDataSource.observeCurrentAccessPort() } @@ -26,19 +29,25 @@ class DefaultAppPreferencesRepository @Inject constructor( localAppPreferencesDataSource.updateGraphSize(size) } - override fun observeLanguage(): Flow { - return localAppPreferencesDataSource.observeLanguage() + override fun observeCurrentLanguage(): Flow { + return localAppPreferencesDataSource.observeLanguage().map { + toAppLanguage(it) + } } - override suspend fun updateLanguage(language: String) { - localAppPreferencesDataSource.updateLanguage(language) + override suspend fun getSupportedLanguages(): List { + return localAppPreferencesDataSource.getSupportedLanguages().map { AppLanguage(it) } + } + + override suspend fun updateLanguage(language: AppLanguage) { + localAppPreferencesDataSource.updateLanguage(language.identifier) } override fun observeProximityLockEnabled(): Flow { return localAppPreferencesDataSource.observeProximityLockEnabled() } - override suspend fun updateProximityLockEnabled(enabled: Boolean) { + override suspend fun updateProximityLockStatus(enabled: Boolean) { localAppPreferencesDataSource.updateProximityLockEnabled(enabled) } @@ -50,3 +59,4 @@ class DefaultAppPreferencesRepository @Inject constructor( localAppPreferencesDataSource.updateAppUiMode(mode) } } + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt index 3891c241..16536ec7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt @@ -16,6 +16,7 @@ import javax.inject.Inject class DefaultLocalAppPreferencesDataSource @Inject constructor( @param:SettingsDataStore private val dataStore: DataStore, + private val systemDataSource: SystemDataSource, ) : LocalAppPreferencesDataSource { override fun observeCurrentAccessPort(): Flow = dataStore.data.map { preferences -> @@ -48,6 +49,10 @@ class DefaultLocalAppPreferencesDataSource @Inject constructor( } } + override suspend fun getSupportedLanguages(): List { + return systemDataSource.getSupportedLanguages() + } + override fun observeProximityLockEnabled(): Flow = dataStore.data.map { preferences -> preferences[PreferencesKeys.PROXIMITY_LOCK_ENABLED] } @@ -67,6 +72,7 @@ class DefaultLocalAppPreferencesDataSource @Inject constructor( preferences[PreferencesKeys.APP_UI_MODE] = mode.identifier } } + private object PreferencesKeys { val ACCESS_PORT = intPreferencesKey("access_port") val GRAPH_SIZE = floatPreferencesKey("graph_size") diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/SystemDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/SystemDataSource.kt new file mode 100644 index 00000000..9afede08 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/SystemDataSource.kt @@ -0,0 +1,12 @@ +package de.rwth_aachen.phyphox.features.settings.data.local + +import de.rwth_aachen.phyphox.BuildConfig +import javax.inject.Inject + +class SystemDataSource @Inject constructor() { + suspend fun getSupportedLanguages(): List { + return BuildConfig.LOCALE_ARRAY + .filterNot { it.contains("+") } + .map { it.replace("-r", "-") } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt index 50da3db9..0d11de3d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/AppPreferencesRepository.kt @@ -1,5 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.domain.data +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow @@ -10,11 +11,12 @@ interface AppPreferencesRepository { fun observeGraphSize(): Flow suspend fun updateGraphSize(size: Float) - fun observeLanguage(): Flow - suspend fun updateLanguage(language: String) + fun observeCurrentLanguage(): Flow + suspend fun updateLanguage(language: AppLanguage) + suspend fun getSupportedLanguages():List fun observeProximityLockEnabled(): Flow - suspend fun updateProximityLockEnabled(enabled: Boolean) + suspend fun updateProximityLockStatus(enabled: Boolean) fun observeAppUiMode(): Flow suspend fun updateAppUiMode(mode: AppUiMode) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt index b9f3c7d7..3ac08f3a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/LocalLanguagePreferencesDataSource.kt @@ -5,4 +5,5 @@ import kotlinx.coroutines.flow.Flow interface LocalLanguagePreferencesDataSource { fun observeLanguage(): Flow suspend fun updateLanguage(language: String) + suspend fun getSupportedLanguages(): List } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/converter/AppLanguageConverter.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/converter/AppLanguageConverter.kt new file mode 100644 index 00000000..472c193c --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/data/local/converter/AppLanguageConverter.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.domain.data.local.converter + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage + + +fun toAppLanguage(identifier: String?): AppLanguage? { + return identifier?.let { + AppLanguage(it) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppLanguage.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppLanguage.kt new file mode 100644 index 00000000..465bfb5d --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/model/AppLanguage.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.domain.model + +data class AppLanguage( + val identifier: String, +){ + companion object{ + const val SYSTEM_DEFAULT_IDENTIFIER = "system_default" + val SYSTEM_DEFAULT = AppLanguage(SYSTEM_DEFAULT_IDENTIFIER) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt index 479fe8e7..f4d42392 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/GetSupportedLanguagesUseCase.kt @@ -1,9 +1,14 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import javax.inject.Inject class GetSupportedLanguagesUseCase @Inject constructor( - private val repository: AppPreferencesRepository -){ + private val repository: AppPreferencesRepository, +) { + + suspend operator fun invoke(): List { + return listOf(AppLanguage.SYSTEM_DEFAULT) + repository.getSupportedLanguages() + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt index b173c5d3..0bc833ca 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/ObserveCurrentAppLanguageUseCase.kt @@ -1,15 +1,15 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -import java.util.Locale +import kotlinx.coroutines.flow.map import javax.inject.Inject class ObserveCurrentAppLanguageUseCase @Inject constructor( - private val repository: AppPreferencesRepository + private val repository: AppPreferencesRepository, ) { - operator fun invoke(): Flow { - return flowOf(Locale.ENGLISH) + operator fun invoke(): Flow { + return repository.observeCurrentLanguage().map { it ?: AppLanguage.SYSTEM_DEFAULT } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt index 26460eb2..1966a693 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/language/UpdateAppLanguageUseCase.kt @@ -1,9 +1,22 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.language import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import javax.inject.Inject class UpdateAppLanguageUseCase @Inject constructor( - private val repository: AppPreferencesRepository -){ + private val repository: AppPreferencesRepository, +) { + suspend operator fun invoke(identifier: String): Result { + return invoke(AppLanguage(identifier)) + } + + suspend operator fun invoke(language: AppLanguage): Result { + return try { + repository.updateLanguage(language) + Result.success(Unit) + } catch (e: Throwable) { + Result.failure(e) + } + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt index c313585a..17850539 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/proximitylock/UpdateProximityLockStatusUseCase.kt @@ -4,9 +4,14 @@ import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesReposi import javax.inject.Inject class UpdateProximityLockStatusUseCase @Inject constructor( - private val repository: AppPreferencesRepository -){ - suspend operator fun invoke(enabled: Boolean) : Result{ - return Result.success(Unit) + private val repository: AppPreferencesRepository, +) { + suspend operator fun invoke(enabled: Boolean): Result { + return try { + repository.updateProximityLockStatus(enabled) + Result.success(Unit) + } catch (error: Throwable) { + Result.failure(error) + } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt new file mode 100644 index 00000000..89a0ad56 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt @@ -0,0 +1,51 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageSheetUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel +import de.rwth_aachen.phyphox.ui.string.resolve + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +fun AppLanguageBottomSheet( + uiModel: AppLanguageSheetUiModel, + onDismiss: () -> Unit, + onConfirm: (identifier:String) -> Unit, + sheetState: SheetState = rememberModalBottomSheetState(), +) { + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = sheetState, + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + .padding(bottom = 32.dp), + ) { + stickyHeader { + Text( + text = stringResource(R.string.settingsLanguage), + style = androidx.compose.material3.MaterialTheme.typography.titleLarge, + ) + } + items(uiModel.availableLocales) { item -> + Text(item.displayName.resolve()) + } + } + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index 7f65acc6..a5461719 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -21,6 +21,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.settingscon import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsUiState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortSheetUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageSheetUiModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme import kotlinx.coroutines.launch @@ -31,7 +32,8 @@ fun SettingsRoot( uiState: SettingsUiState, onActionEvent: (SettingsAction) -> Unit, ) { - val sheetState = rememberModalBottomSheetState() + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val coroutineScope = rememberCoroutineScope() LaunchedEffect(uiState.modal) { if (uiState.modal == null) { @@ -66,8 +68,7 @@ fun SettingsRoot( accessPort = uiState.accessPort, proximityLockEnabled = uiState.proximityLockEnabled, onActionEvent = onActionEvent, - - ) + ) } when (val modal = uiState.modal) { is AccessPortSheetUiModel -> AccessPortBottomSheet( @@ -83,9 +84,24 @@ fun SettingsRoot( onActionEvent.invoke(SettingsAction.OnAccessPortChanged(newPort)) }, ) + + is AppLanguageSheetUiModel -> AppLanguageBottomSheet( + uiModel = modal, + onDismiss = { + coroutineScope.launch { + sheetState.hide() + onActionEvent.invoke(SettingsAction.OnModalDismissed) + } + }, + onConfirm = { newLanguage -> + onActionEvent.invoke(SettingsAction.OnAppLanguageChanged(newLanguage)) + }, + + ) } } + @Composable @PreviewLightDark internal fun SettingsRootPreview() { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt index c671d74c..7379a98e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel @@ -53,6 +54,40 @@ fun ClickablePreferenceItem( ) } +@Composable +fun LanguagePreferenceItem( + modifier: Modifier = Modifier, + title: StringUIModel, + summary: UiResourceState, + iconRes: Int? = null, + onClick: () -> Unit = {}, +) { + + PreferenceItem( + modifier = modifier.clickable( + enabled = summary is UiResourceState.Success, + onClick = onClick, + ), + title = title, + iconRes = iconRes, + content = { + when (summary) { + UiResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 4.dp) + .fillMaxWidth() + .height(16.dp) + .skeleton(), + ) + + is UiResourceState.Success -> PreferenceSummaryItem( + text = summary.data.displayName, + ) + } + }, + ) +} + @Preview(showBackground = true) @Composable internal fun ClickablePreferenceItemPreview( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index d2b27ec8..b443741c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.ClickablePreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.LanguagePreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem @@ -30,6 +31,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbu import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @@ -39,7 +41,7 @@ import de.rwth_aachen.phyphox.utils.UiResourceState @Composable fun SettingsContent( modifier: Modifier = Modifier, - currentLanguage: UiResourceState, + currentLanguage: UiResourceState, seekbarConfig: UiResourceState, appUiMode: UiResourceState>>, accessPort: UiResourceState, @@ -74,7 +76,7 @@ fun SettingsContent( ) } item { - ClickablePreferenceItem( + LanguagePreferenceItem( title = ResourceStringUIModel(resId = R.string.settingsLanguage), summary = currentLanguage, iconRes = R.drawable.setting_language, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt index e80ec419..08f3ebb6 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -9,8 +9,8 @@ sealed interface SettingsAction { data class OnGraphSizeChanged(val size: Float) : SettingsAction data class OnProximityLockChanged(val enabled: Boolean) : SettingsAction data class OnAccessPortChanged(val newPort: String) : SettingsAction + data class OnAppLanguageChanged(val newLanguageIdentifier: String) : SettingsAction data object OnModalDismissed : SettingsAction - data object OnAppLanguageClicked : SettingsAction data object OnLearnMoreAboutTranslationClicked : SettingsAction data object OnAccessPortClicked : SettingsAction diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt index bd08cd61..4fd112d4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsSheetUiModel.kt @@ -1,5 +1,3 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel -import de.rwth_aachen.phyphox.ui.string.StringUIModel - interface SettingsSheetUiModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 1d949901..53457835 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -4,11 +4,12 @@ import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState data class SettingsUiState( - val currentLanguage: UiResourceState = UiResourceState.Loading, + val currentLanguage: UiResourceState = UiResourceState.Loading, val graphSize: UiResourceState = UiResourceState.Loading, val appUiMode: UiResourceState>> = UiResourceState.Loading, val accessPort: UiResourceState = UiResourceState.Loading, @@ -29,7 +30,7 @@ data class SettingsUiState( } data class UserSettings( - val currentLanguage: UiResourceState = UiResourceState.Loading, + val currentLanguage: UiResourceState = UiResourceState.Loading, val graphSize: UiResourceState = UiResourceState.Loading, val appUiMode: UiResourceState>> = UiResourceState.Loading, val accessPort: UiResourceState = UiResourceState.Loading, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt index eb662a48..560d18f8 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt @@ -4,7 +4,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.* +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.NavigateBack +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.OpenWebpageFromResourceID import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate @@ -41,8 +42,6 @@ internal class SettingsViewModel @Inject constructor( start(viewModelScope) } - val uiModalFlow = accessPortDelegate.inputModal - override fun start(scope: CoroutineScope) { accessPortDelegate.start(scope) appLanguageDelegate.start(scope) @@ -51,9 +50,15 @@ internal class SettingsViewModel @Inject constructor( proximityLockDelegate.start(scope) } + private val uiModalFlow = combine( + accessPortDelegate.portInputModal, + appLanguageDelegate.languageSelectionModal, + ) { modals: Array -> + modals.firstOrNull { it != null } + } val uiState = combine( accessPortDelegate.accessPortFlow, - appLanguageDelegate.appLanguageFlow, + appLanguageDelegate.currentAppLanguageFlow, appUiModeDelegate.appUiModeFlow, graphSizeDelegate.graphSizeFlow, proximityLockDelegate.proximityLockFlow, @@ -77,34 +82,33 @@ internal class SettingsViewModel @Inject constructor( ) fun onActionEvent(action: SettingsAction) { - when (action) { - SettingsAction.OnAccessPortClicked -> { - viewModelScope.launch { - accessPortDelegate.showAccessPortInputModal() - } - } - SettingsAction.OnAppLanguageClicked -> appLanguageDelegate::showLanguagePickerModal - SettingsAction.OnBackPressed -> sendEvent(NavigateBack) - is SettingsAction.OnGraphSizeChanged -> graphSizeDelegate::updateGraphSize - SettingsAction.OnLearnMoreAboutTranslationClicked -> sendEvent( - OpenWebpageFromResourceID( - R.string.settingsTranslation, - ), - ) - - is SettingsAction.OnProximityLockChanged -> proximityLockDelegate::updateProximityLockStatus - is SettingsAction.OnUiModeItemSelected -> appUiModeDelegate::updateAppUiMode - is SettingsAction.OnAccessPortChanged -> viewModelScope.launch { - accessPortDelegate.setAccessPort(action.newPort) - } - - SettingsAction.OnModalDismissed -> viewModelScope.launch { - accessPortDelegate.dismissAccessPortInputModal() + viewModelScope.launch { + when (action) { + is SettingsAction.OnGraphSizeChanged -> graphSizeDelegate.updateGraphSize(action.size) + is SettingsAction.OnProximityLockChanged -> proximityLockDelegate.updateProximityLockStatus(action.enabled) + is SettingsAction.OnUiModeItemSelected -> appUiModeDelegate.updateAppUiMode(action.appUiMode) + is SettingsAction.OnAccessPortChanged -> accessPortDelegate.setAccessPort(action.newPort) + is SettingsAction.OnAppLanguageChanged -> appLanguageDelegate.updateLanguage(action.newLanguageIdentifier) + SettingsAction.OnAccessPortClicked -> accessPortDelegate.showAccessPortInputModal() + SettingsAction.OnAppLanguageClicked -> appLanguageDelegate.showLanguagePickerModal() + SettingsAction.OnModalDismissed -> dismissModal() + SettingsAction.OnBackPressed -> sendEvent(NavigateBack) + SettingsAction.OnLearnMoreAboutTranslationClicked -> sendEvent( + OpenWebpageFromResourceID( + R.string.settingsTranslation, + ), + ) } } } + private fun sendEvent(event: SettingsEvent) = viewModelScope.launch { _uiEvent.emit(event) } + + override fun dismissModal() { + accessPortDelegate.dismissModal() + appLanguageDelegate.dismissModal() + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index 727a69a7..a0dce9a0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -1,10 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.domain.model.errors.AccessPortOutOfRange import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -15,17 +17,30 @@ import javax.inject.Inject internal class UiBuilder @Inject constructor() { //region - App Language - fun buildLanguageUiModel(localeResource: UiResourceState): UiResourceState { - return when (localeResource) { + fun buildLanguageUiModel(languageResource: UiResourceState): UiResourceState { + return when (languageResource) { UiResourceState.Loading -> UiResourceState.Loading - is UiResourceState.Success -> UiResourceState.Success( - mapLocaleToUiModel(localeResource.data), + is UiResourceState.Success -> UiResourceState.Success( + buildLanguageUiModel(languageResource.data), ) } } - private fun mapLocaleToUiModel(locale: Locale): StringUIModel { - return locale.displayName.toStringUIModel() + fun buildLanguageUiModel(appLanguage: AppLanguage): LanguageUiModel { + return if (appLanguage.identifier == AppLanguage.SYSTEM_DEFAULT_IDENTIFIER) { + LanguageUiModel( + identifier = AppLanguage.SYSTEM_DEFAULT_IDENTIFIER, + displayName = R.string.settingsDefault.toStringUIModel(), + ) + } else { + val locale = Locale.forLanguageTag(appLanguage.identifier) + LanguageUiModel( + identifier = AppLanguage.SYSTEM_DEFAULT_IDENTIFIER, + displayName = locale.displayName.toStringUIModel(), + localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), + displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), + ) + } } //endregion diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt new file mode 100644 index 00000000..b9e151eb --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt @@ -0,0 +1,9 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates + +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow + +interface SettingsDelegate { + fun start(scope: CoroutineScope) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt index f1b34412..6acb1544 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt @@ -1,21 +1,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface AccessPortDelegate { +interface AccessPortDelegate : SettingsDelegate { val accessPortFlow: Flow> - val inputModal: Flow - fun start(scope: CoroutineScope) + val portInputModal: Flow suspend fun showAccessPortInputModal() - suspend fun setAccessPort(newPort:String) - - suspend fun dismissAccessPortInputModal() - + suspend fun setAccessPort(newPort: String) + fun dismissModal() } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt index b4ff9960..98c81ffd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt @@ -3,6 +3,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeApplicationService import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -31,7 +32,7 @@ internal class DefaultAccessPortDelegate @Inject constructor( } private val inputModalStaFlow = MutableStateFlow(null) - override val inputModal: Flow = inputModalStaFlow + override val portInputModal: Flow = inputModalStaFlow override fun start(scope: CoroutineScope) { observeCurrentAccessPort().onStart { @@ -65,7 +66,7 @@ internal class DefaultAccessPortDelegate @Inject constructor( } } - override suspend fun dismissAccessPortInputModal() { + override fun dismissModal() { inputModalStaFlow.value = null } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt index 25e1de7b..534ebc24 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt @@ -1,13 +1,17 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage -import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface AppLanguageDelegate { - val appLanguageFlow: Flow> - fun start(scope: CoroutineScope) +interface AppLanguageDelegate : SettingsDelegate { + + val currentAppLanguageFlow: Flow> + + val languageSelectionModal: Flow suspend fun showLanguagePickerModal() + + suspend fun updateLanguage(identifier: String) + fun dismissModal() } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageSheetUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageSheetUiModel.kt new file mode 100644 index 00000000..c779bb04 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageSheetUiModel.kt @@ -0,0 +1,13 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class AppLanguageSheetUiModel( + val title: StringUIModel, + val currentSelection: AppLanguage, + val availableLocales: List, +) : SettingsSheetUiModel + + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index ea34ec73..cf687202 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -1,10 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage +import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.GetSupportedLanguagesUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.UpdateAppLanguageUseCase import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder -import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -13,7 +15,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart -import java.util.Locale import javax.inject.Inject internal class DefaultAppLanguageDelegate @Inject constructor( @@ -22,10 +23,14 @@ internal class DefaultAppLanguageDelegate @Inject constructor( private val updateAppLanguage: UpdateAppLanguageUseCase, private val uiBuilder: UiBuilder, ) : AppLanguageDelegate { - private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) - override val appLanguageFlow: Flow> = currentLanguageFlow.map { localeResource -> - uiBuilder.buildLanguageUiModel(localeResource) - } + private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) + override val currentAppLanguageFlow: Flow> = + currentLanguageFlow.map { appLanguage -> + uiBuilder.buildLanguageUiModel(appLanguage) + } + private val inputModalStaFlow = MutableStateFlow(null) + + override val languageSelectionModal: Flow = inputModalStaFlow override fun start(scope: CoroutineScope) { observeCurrentAppLanguage().onStart { @@ -36,6 +41,25 @@ internal class DefaultAppLanguageDelegate @Inject constructor( } override suspend fun showLanguagePickerModal() { + val currentLocale = when (val temp = currentLanguageFlow.value) { + UiResourceState.Loading -> null + is UiResourceState.Success -> temp.data + } + val supportedLanguages = getSupportedLanguages().map { + uiBuilder.buildLanguageUiModel(it) + } + inputModalStaFlow.value = AppLanguageSheetUiModel( + title = R.string.settingsLanguage.toStringUIModel(), + currentSelection = currentLocale ?: AppLanguage.SYSTEM_DEFAULT, + availableLocales = supportedLanguages, + ) + } + + override suspend fun updateLanguage(identifier: String) { + updateAppLanguage(identifier) + } + override fun dismissModal() { + inputModalStaFlow.value = null } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/LanguageUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/LanguageUiModel.kt new file mode 100644 index 00000000..19b7d038 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/LanguageUiModel.kt @@ -0,0 +1,10 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage + +import de.rwth_aachen.phyphox.ui.string.StringUIModel + +data class LanguageUiModel( + val identifier: String, + val displayName: StringUIModel, + val localDisplayName: StringUIModel? = null, + val displayCountry: StringUIModel? = null, +) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt index 2f30f521..3e633c08 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt @@ -2,13 +2,13 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface AppUiModeDelegate { +interface AppUiModeDelegate : SettingsDelegate { val appUiModeFlow: Flow>>> - fun start(scope: CoroutineScope) suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt index cc947289..46ec65ba 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt @@ -1,12 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface GraphSizeDelegate { +interface GraphSizeDelegate : SettingsDelegate{ val graphSizeFlow: Flow> - fun start(scope: CoroutineScope) suspend fun updateGraphSize(size: Float) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt index a70d0b78..769c68ef 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt @@ -1,13 +1,12 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface ProximityLockDelegate { +interface ProximityLockDelegate: SettingsDelegate { val proximityLockFlow: Flow> - fun start(scope: CoroutineScope) - suspend fun updateProximityLockStatus(enabled: Boolean) } From 248c716ea5f58db62ce2f845c30e7f7562b153c9 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 00:03:12 +0100 Subject: [PATCH 100/111] do some sorting --- .../compose/AppLanguageBottomSheet.kt | 3 ++ .../ClickablePreferenceItem.kt | 35 +------------ .../LanguagePreferenceItem.kt | 51 +++++++++++++++++++ .../PreferenceSummaryItem.kt | 34 ++++++++++--- .../SeekBarPreferenceItem.kt | 2 +- .../SegmentedButtonPreferenceItem.kt | 2 +- .../SwitchPreferenceItem.kt | 2 +- .../presentation/viewmodel/UiBuilder.kt | 35 ++++++++++--- .../applanguage/DefaultAppLanguageDelegate.kt | 4 +- 9 files changed, 113 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt index 89a0ad56..db9bc1a3 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt @@ -15,9 +15,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.resolve +import kotlin.collections.sortedWith @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable @@ -27,6 +29,7 @@ fun AppLanguageBottomSheet( onConfirm: (identifier:String) -> Unit, sheetState: SheetState = rememberModalBottomSheetState(), ) { + ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt index 7379a98e..04d95de9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel @@ -47,46 +46,14 @@ fun ClickablePreferenceItem( ) is UiResourceState.Success -> PreferenceSummaryItem( - text = summary.data, + primaryText = summary.data, ) } }, ) } -@Composable -fun LanguagePreferenceItem( - modifier: Modifier = Modifier, - title: StringUIModel, - summary: UiResourceState, - iconRes: Int? = null, - onClick: () -> Unit = {}, -) { - PreferenceItem( - modifier = modifier.clickable( - enabled = summary is UiResourceState.Success, - onClick = onClick, - ), - title = title, - iconRes = iconRes, - content = { - when (summary) { - UiResourceState.Loading -> Box( - modifier = Modifier - .padding(top = 4.dp) - .fillMaxWidth() - .height(16.dp) - .skeleton(), - ) - - is UiResourceState.Success -> PreferenceSummaryItem( - text = summary.data.displayName, - ) - } - }, - ) -} @Preview(showBackground = true) @Composable diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt new file mode 100644 index 00000000..ba6784e5 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt @@ -0,0 +1,51 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel +import de.rwth_aachen.phyphox.ui.skeleton +import de.rwth_aachen.phyphox.ui.string.StringUIModel +import de.rwth_aachen.phyphox.utils.UiResourceState + +@Composable +fun LanguagePreferenceItem( + modifier: Modifier = Modifier, + title: StringUIModel, + summary: UiResourceState, + iconRes: Int? = null, + onClick: () -> Unit = {}, +) { + + PreferenceItem( + modifier = modifier.clickable( + enabled = summary is UiResourceState.Success, + onClick = onClick, + ), + title = title, + iconRes = iconRes, + content = { + when (summary) { + UiResourceState.Loading -> Box( + modifier = Modifier + .padding(top = 4.dp) + .fillMaxWidth() + .height(16.dp) + .skeleton(), + ) + + is UiResourceState.Success -> PreferenceSummaryItem( + primaryText = summary.data.displayName, + secondaryText = summary.data.displayCountry, + ) + } + }, + ) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt index 4908a7a4..e136759c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt @@ -1,10 +1,13 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.resolve @@ -14,14 +17,29 @@ import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @Composable fun PreferenceSummaryItem( modifier: Modifier = Modifier, - text: StringUIModel, + primaryText: StringUIModel, + secondaryText: StringUIModel? = null, ) { - Text( - modifier = modifier, - text = text.resolve(), - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) + + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + modifier = modifier, + text = primaryText.resolve(), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + secondaryText?.let { + Text( + modifier = modifier, + text = it.resolve(), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } @PreviewLightDark @@ -29,7 +47,7 @@ fun PreferenceSummaryItem( internal fun PreferenceSummaryItemPreview() { PhyphoxTheme { PreferenceSummaryItem( - text = LoremIpsumStringUIModel(4), + primaryText = LoremIpsumStringUIModel(4), ) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt index 9fa3f3e0..a58e9bec 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -34,7 +34,7 @@ fun SeekBarPreferenceItem( iconRes = iconRes, content = { summary?.let { - PreferenceSummaryItem(text = summary) + PreferenceSummaryItem(primaryText = summary) } when (seekBarConfig) { UiResourceState.Loading -> Box( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index ccc28746..f01df62b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -38,7 +38,7 @@ fun SegmentedButtonPreferenceItem( iconRes = iconRes, content = { summary?.let { - PreferenceSummaryItem(text = summary) + PreferenceSummaryItem(primaryText = summary) } when (config) { UiResourceState.Loading -> Box( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt index 7e674f14..7ccad54a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt @@ -42,7 +42,7 @@ fun SwitchPreferenceItem( iconRes = iconRes, content = { summary?.let { - PreferenceSummaryItem(text = summary) + PreferenceSummaryItem(primaryText = summary) } }, trailingContent = { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index a0dce9a0..d7f59cf9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -11,12 +11,27 @@ import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import java.util.Locale import javax.inject.Inject -internal class UiBuilder @Inject constructor() { +internal class UiBuilder @Inject constructor( + +) { //region - App Language + fun getSortedLanguageModels(supportedLanguages: List): List { + return listOf( + buildLanguageUiModel(AppLanguage.SYSTEM_DEFAULT), + ) + supportedLanguages.filter { it != AppLanguage.SYSTEM_DEFAULT } + .map { + Locale.forLanguageTag(it.identifier) + }.sortedBy { it.displayName }.map { + localeToLanguageUiModel(it) + } + } + fun buildLanguageUiModel(languageResource: UiResourceState): UiResourceState { return when (languageResource) { UiResourceState.Loading -> UiResourceState.Loading @@ -34,14 +49,18 @@ internal class UiBuilder @Inject constructor() { ) } else { val locale = Locale.forLanguageTag(appLanguage.identifier) - LanguageUiModel( - identifier = AppLanguage.SYSTEM_DEFAULT_IDENTIFIER, - displayName = locale.displayName.toStringUIModel(), - localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), - displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), - ) + localeToLanguageUiModel(locale) } } + + fun localeToLanguageUiModel(locale: Locale): LanguageUiModel { + return LanguageUiModel( + identifier = AppLanguage.SYSTEM_DEFAULT_IDENTIFIER, + displayName = locale.displayName.toStringUIModel(), + localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), + displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), + ) + } //endregion //region - AppUiMode @@ -113,5 +132,7 @@ internal class UiBuilder @Inject constructor() { else -> "Unknown error".toStringUIModel() } } + + //endregion } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index cf687202..55370a82 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -45,9 +45,7 @@ internal class DefaultAppLanguageDelegate @Inject constructor( UiResourceState.Loading -> null is UiResourceState.Success -> temp.data } - val supportedLanguages = getSupportedLanguages().map { - uiBuilder.buildLanguageUiModel(it) - } + val supportedLanguages = uiBuilder.getSortedLanguageModels(getSupportedLanguages()) inputModalStaFlow.value = AppLanguageSheetUiModel( title = R.string.settingsLanguage.toStringUIModel(), currentSelection = currentLocale ?: AppLanguage.SYSTEM_DEFAULT, From d858e025e77803b95213a96af52603e4847aa8c0 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 00:11:27 +0100 Subject: [PATCH 101/111] do some sorting --- .../presentation/viewmodel/UiBuilder.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index d7f59cf9..e0d47119 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -11,8 +11,6 @@ import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import java.util.Locale import javax.inject.Inject @@ -22,14 +20,13 @@ internal class UiBuilder @Inject constructor( //region - App Language fun getSortedLanguageModels(supportedLanguages: List): List { - return listOf( - buildLanguageUiModel(AppLanguage.SYSTEM_DEFAULT), - ) + supportedLanguages.filter { it != AppLanguage.SYSTEM_DEFAULT } - .map { - Locale.forLanguageTag(it.identifier) - }.sortedBy { it.displayName }.map { - localeToLanguageUiModel(it) - } + val systemDefaultModel = buildLanguageUiModel(AppLanguage.SYSTEM_DEFAULT) + val otherLanguageModels = supportedLanguages + .filter { it != AppLanguage.SYSTEM_DEFAULT } + .map { Locale.forLanguageTag(it.identifier) } + .sortedBy { it.displayName } + .map{ localeToLanguageUiModel(it)} + return listOf(systemDefaultModel) + otherLanguageModels } fun buildLanguageUiModel(languageResource: UiResourceState): UiResourceState { From e5ec2cc16d3c72ed12cbd042c93d10da504ba948 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 00:15:59 +0100 Subject: [PATCH 102/111] identifier bug fix --- .../features/settings/presentation/viewmodel/UiBuilder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index e0d47119..ab4ce455 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -52,7 +52,7 @@ internal class UiBuilder @Inject constructor( fun localeToLanguageUiModel(locale: Locale): LanguageUiModel { return LanguageUiModel( - identifier = AppLanguage.SYSTEM_DEFAULT_IDENTIFIER, + identifier = locale.toLanguageTag(), displayName = locale.displayName.toStringUIModel(), localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), From e09be4509cbf47b4b80b6aadb08c76378d96a861 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 00:17:49 +0100 Subject: [PATCH 103/111] identifier bug fix --- .../presentation/viewmodel/UiBuilder.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index ab4ce455..d1b29db0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -23,9 +23,14 @@ internal class UiBuilder @Inject constructor( val systemDefaultModel = buildLanguageUiModel(AppLanguage.SYSTEM_DEFAULT) val otherLanguageModels = supportedLanguages .filter { it != AppLanguage.SYSTEM_DEFAULT } - .map { Locale.forLanguageTag(it.identifier) } - .sortedBy { it.displayName } - .map{ localeToLanguageUiModel(it)} + .map { + Pair( + it.identifier, + Locale.forLanguageTag(it.identifier), + ) + } + .sortedBy { it.second.displayName } + .map { localeToLanguageUiModel(it.first, it.second) } return listOf(systemDefaultModel) + otherLanguageModels } @@ -46,13 +51,13 @@ internal class UiBuilder @Inject constructor( ) } else { val locale = Locale.forLanguageTag(appLanguage.identifier) - localeToLanguageUiModel(locale) + localeToLanguageUiModel(appLanguage.identifier, locale) } } - fun localeToLanguageUiModel(locale: Locale): LanguageUiModel { + fun localeToLanguageUiModel(identifier: String, locale: Locale): LanguageUiModel { return LanguageUiModel( - identifier = locale.toLanguageTag(), + identifier = identifier, displayName = locale.displayName.toStringUIModel(), localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), From 464bd57d87f3e369a4754c836cc9a8f3ec00ccfe Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 00:19:54 +0100 Subject: [PATCH 104/111] skip partially expanded --- .../features/settings/presentation/compose/SettingsRoot.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt index a5461719..3d755a44 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/SettingsRoot.kt @@ -96,8 +96,8 @@ fun SettingsRoot( onConfirm = { newLanguage -> onActionEvent.invoke(SettingsAction.OnAppLanguageChanged(newLanguage)) }, - - ) + sheetState = sheetState, + ) } } From 41f55d318fa3b4bdd883c7530c9bbe9b05b049dd Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 15:38:04 +0100 Subject: [PATCH 105/111] language delegate to observe user selection and set language --- .../main/java/de/rwth_aachen/phyphox/App.kt | 15 ++++ .../phyphox/appdelegate/LanguageDelegate.kt | 28 +++++++ .../compose/AppLanguageBottomSheet.kt | 78 +++++++++++++++++-- .../applanguage/DefaultAppLanguageDelegate.kt | 1 + 4 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt diff --git a/app/src/main/java/de/rwth_aachen/phyphox/App.kt b/app/src/main/java/de/rwth_aachen/phyphox/App.kt index b16162f1..87812b86 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/App.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/App.kt @@ -2,12 +2,27 @@ package de.rwth_aachen.phyphox import androidx.multidex.MultiDexApplication import dagger.hilt.android.HiltAndroidApp +import de.rwth_aachen.phyphox.appdelegate.LanguageDelegate +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import javax.inject.Inject //This extension to application is only used to store measured data in memory as this may easily exceed the amount of data allowed on the transaction stack @HiltAndroidApp class App : MultiDexApplication() { + private val applicationScope = CoroutineScope(SupervisorJob()) + + @Inject + lateinit var languageDelegate: LanguageDelegate + + //Need to get rid off of this ASAP @JvmField var experiment: PhyphoxExperiment? = null + + override fun onCreate() { + super.onCreate() + languageDelegate.start(applicationScope) + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt new file mode 100644 index 00000000..758b6140 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt @@ -0,0 +1,28 @@ +package de.rwth_aachen.phyphox.appdelegate + +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.os.LocaleListCompat +import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage +import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + + +class LanguageDelegate @Inject constructor( + private val observeCurrentAppLanguageUseCase: ObserveCurrentAppLanguageUseCase, +) { + fun start(scope: CoroutineScope) { + observeCurrentAppLanguageUseCase() + .onEach { appLanguage -> + val localeList = + if (appLanguage.identifier == AppLanguage.SYSTEM_DEFAULT_IDENTIFIER) { // Use system default if the code is empty + LocaleListCompat.getEmptyLocaleList() + } else { + LocaleListCompat.forLanguageTags(appLanguage.identifier) + } + AppCompatDelegate.setApplicationLocales(localeList) + }.launchIn(scope) + } +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt index db9bc1a3..dd208adc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/AppLanguageBottomSheet.kt @@ -1,32 +1,42 @@ package de.rwth_aachen.phyphox.features.settings.presentation.compose import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.resolve -import kotlin.collections.sortedWith @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun AppLanguageBottomSheet( uiModel: AppLanguageSheetUiModel, onDismiss: () -> Unit, - onConfirm: (identifier:String) -> Unit, + onConfirm: (identifier: String) -> Unit, sheetState: SheetState = rememberModalBottomSheetState(), ) { @@ -36,18 +46,72 @@ fun AppLanguageBottomSheet( ) { LazyColumn( modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 24.dp) - .padding(bottom = 32.dp), + .fillMaxWidth(), + userScrollEnabled = true, + verticalArrangement = Arrangement.spacedBy(8.dp), ) { stickyHeader { Text( + modifier = Modifier + .fillMaxWidth() + .background( + MaterialTheme.colorScheme.surfaceContainerLow, + ) + .padding(16.dp), text = stringResource(R.string.settingsLanguage), style = androidx.compose.material3.MaterialTheme.typography.titleLarge, ) } items(uiModel.availableLocales) { item -> - Text(item.displayName.resolve()) + LanguageListItem( + language = item, + isSelected = item.identifier == uiModel.currentSelection.identifier, + onClick = { + onConfirm(item.identifier) + }, + ) + } + } + } +} + + +@Composable +private fun LanguageListItem( + modifier: Modifier = Modifier, + language: LanguageUiModel, + isSelected: Boolean, + onClick: () -> Unit, +) { + Row( + modifier = modifier + .fillMaxWidth() + .clickable(onClick = onClick) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + if (isSelected) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource(R.string.selected), + tint = MaterialTheme.colorScheme.primary, + ) + } else { + Spacer(modifier = Modifier.width(24.dp)) + } + + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.Center, + ) { + Text(language.displayName.resolve()) + language.localDisplayName?.let { + Text( + text = it.resolve(), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) } } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt index 55370a82..1a50bc29 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt @@ -55,6 +55,7 @@ internal class DefaultAppLanguageDelegate @Inject constructor( override suspend fun updateLanguage(identifier: String) { updateAppLanguage(identifier) + dismissModal() } override fun dismissModal() { From 2c6ff20a837d24d706fceea0c453c63bcfbf7b14 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 15:43:43 +0100 Subject: [PATCH 106/111] fix the wrong selection being displayed --- .../settings/data/local/DefaultLocalAppPreferencesDataSource.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt index 16536ec7..7b6decc5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/data/local/DefaultLocalAppPreferencesDataSource.kt @@ -13,7 +13,9 @@ import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import javax.inject.Inject +import javax.inject.Singleton +@Singleton class DefaultLocalAppPreferencesDataSource @Inject constructor( @param:SettingsDataStore private val dataStore: DataStore, private val systemDataSource: SystemDataSource, From c5840ce36fa775df46b427d66e16b7f7e4eab260 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 17:24:49 +0100 Subject: [PATCH 107/111] settings localized display name as primary --- .../features/settings/presentation/viewmodel/UiBuilder.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index d1b29db0..e0169e12 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -58,8 +58,8 @@ internal class UiBuilder @Inject constructor( fun localeToLanguageUiModel(identifier: String, locale: Locale): LanguageUiModel { return LanguageUiModel( identifier = identifier, - displayName = locale.displayName.toStringUIModel(), - localDisplayName = locale.getDisplayLanguage(locale).toStringUIModel(), + displayName = locale.getDisplayName(locale).toStringUIModel(), + localDisplayName = locale.displayName.toStringUIModel(), displayCountry = locale.getDisplayCountry(locale).toStringUIModel(), ) } From 596fcd9ae104e867e2cc7aaed6a576409cd02199 Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 21:25:41 +0100 Subject: [PATCH 108/111] app delegate --- .../main/java/de/rwth_aachen/phyphox/App.kt | 15 ++++-- .../rwth_aachen/phyphox/common/AppDelegate.kt | 8 ++++ .../features/settings/di/SettingsModule.kt | 48 +++++++++++-------- .../presentation/AppLanguageDelegate.kt} | 16 ++++--- .../presentation/NewSettingsActivity.kt | 4 +- ...Model.kt => SettingsViewmodelViewModel.kt} | 32 ++++++------- ...legate.kt => SettingsViewmodelDelegate.kt} | 4 +- ...gate.kt => AccessPortViewmodelDelegate.kt} | 4 +- ... => DefaultAccessPortViewmodelDelegate.kt} | 5 +- ...ate.kt => AppLanguageViewmodelDelegate.kt} | 4 +- ...=> DefaultAppLanguageViewmodelDelegate.kt} | 4 +- ...egate.kt => AppUiModeViewmodelDelegate.kt} | 5 +- ...t => DefaultAppUiModeViewmodelDelegate.kt} | 4 +- ...t => DefaultGraphSizeViewmodelDelegate.kt} | 4 +- ...egate.kt => GraphSizeViewmodelDelegate.kt} | 5 +- ... DefaultProximityLockViewmodelDelegate.kt} | 4 +- ...e.kt => ProximityLockViewmodelDelegate.kt} | 5 +- 17 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/common/AppDelegate.kt rename app/src/main/java/de/rwth_aachen/phyphox/{appdelegate/LanguageDelegate.kt => features/settings/presentation/AppLanguageDelegate.kt} (74%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/{SettingsViewModel.kt => SettingsViewmodelViewModel.kt} (81%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/{SettingsDelegate.kt => SettingsViewmodelDelegate.kt} (51%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/{AccessPortDelegate.kt => AccessPortViewmodelDelegate.kt} (82%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/{DefaultAccessPortDelegate.kt => DefaultAccessPortViewmodelDelegate.kt} (94%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/{AppLanguageDelegate.kt => AppLanguageViewmodelDelegate.kt} (81%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/{DefaultAppLanguageDelegate.kt => DefaultAppLanguageViewmodelDelegate.kt} (96%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/{AppUiModeDelegate.kt => AppUiModeViewmodelDelegate.kt} (83%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/{DefaultAppUiModeDelegate.kt => DefaultAppUiModeViewmodelDelegate.kt} (96%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/{DefaultGraphSizeDelegate.kt => DefaultGraphSizeViewmodelDelegate.kt} (95%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/{GraphSizeDelegate.kt => GraphSizeViewmodelDelegate.kt} (78%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/{DefaultProximityLockDelegate.kt => DefaultProximityLockViewmodelDelegate.kt} (93%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/{ProximityLockDelegate.kt => ProximityLockViewmodelDelegate.kt} (74%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/App.kt b/app/src/main/java/de/rwth_aachen/phyphox/App.kt index 87812b86..8d678c60 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/App.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/App.kt @@ -2,7 +2,7 @@ package de.rwth_aachen.phyphox import androidx.multidex.MultiDexApplication import dagger.hilt.android.HiltAndroidApp -import de.rwth_aachen.phyphox.appdelegate.LanguageDelegate +import de.rwth_aachen.phyphox.common.AppDelegate import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import javax.inject.Inject @@ -14,7 +14,8 @@ class App : MultiDexApplication() { private val applicationScope = CoroutineScope(SupervisorJob()) @Inject - lateinit var languageDelegate: LanguageDelegate + lateinit var appDelegates: Set<@JvmSuppressWildcards AppDelegate> + //Need to get rid off of this ASAP @@ -23,6 +24,14 @@ class App : MultiDexApplication() { override fun onCreate() { super.onCreate() - languageDelegate.start(applicationScope) + appDelegates.forEach { it.start(applicationScope) } + } + + override fun onTerminate() { + appDelegates.forEach { it.stop() } + super.onTerminate() } + } + + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/common/AppDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/common/AppDelegate.kt new file mode 100644 index 00000000..7a1583a2 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/common/AppDelegate.kt @@ -0,0 +1,8 @@ +package de.rwth_aachen.phyphox.common + +import kotlinx.coroutines.CoroutineScope + +interface AppDelegate { + fun start(coroutineScope: CoroutineScope) + fun stop() +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt index d6f87b11..4ab1fc0d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -11,20 +11,24 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import dagger.multibindings.IntoSet +import de.rwth_aachen.phyphox.common.AppDelegate import de.rwth_aachen.phyphox.features.settings.data.DefaultAppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.data.local.DefaultLocalAppPreferencesDataSource import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.data.local.LocalAppPreferencesDataSource -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.DefaultAccessPortDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.DefaultAppLanguageDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.DefaultAppUiModeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.DefaultGraphSizeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.DefaultProximityLockDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.AppLanguageDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.DefaultAccessPortViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.DefaultAppLanguageViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.DefaultAppUiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.DefaultGraphSizeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.DefaultProximityLockViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockViewmodelDelegate +import kotlinx.coroutines.CoroutineScope import javax.inject.Qualifier import javax.inject.Singleton @@ -34,28 +38,28 @@ abstract class SettingsModule { @Binds internal abstract fun bindsAccessPortDelegate( - implementation: DefaultAccessPortDelegate, - ): AccessPortDelegate + implementation: DefaultAccessPortViewmodelDelegate, + ): AccessPortViewmodelDelegate @Binds internal abstract fun bindsAppLanguageDelegate( - implementation: DefaultAppLanguageDelegate, - ): AppLanguageDelegate + implementation: DefaultAppLanguageViewmodelDelegate, + ): AppLanguageViewmodelDelegate @Binds internal abstract fun bindsAppUiModeDelegate( - implementation: DefaultAppUiModeDelegate, - ): AppUiModeDelegate + implementation: DefaultAppUiModeViewmodelDelegate, + ): AppUiModeViewmodelDelegate @Binds internal abstract fun bindsGraphSizeDelegate( - implementation: DefaultGraphSizeDelegate, - ): GraphSizeDelegate + implementation: DefaultGraphSizeViewmodelDelegate, + ): GraphSizeViewmodelDelegate @Binds internal abstract fun bindsProximityLockDelegate( - implementation: DefaultProximityLockDelegate, - ): ProximityLockDelegate + implementation: DefaultProximityLockViewmodelDelegate, + ): ProximityLockViewmodelDelegate @Binds internal abstract fun bindLocalDataSource( @@ -67,6 +71,10 @@ abstract class SettingsModule { implementation: DefaultAppPreferencesRepository, ): AppPreferencesRepository + @Binds + @Singleton + @IntoSet + abstract fun provideAppLanguageDelegate(implementation: AppLanguageDelegate): AppDelegate companion object { @Provides diff --git a/app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/AppLanguageDelegate.kt similarity index 74% rename from app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/AppLanguageDelegate.kt index 758b6140..fb740b7a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/appdelegate/LanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/AppLanguageDelegate.kt @@ -1,7 +1,8 @@ -package de.rwth_aachen.phyphox.appdelegate +package de.rwth_aachen.phyphox.features.settings.presentation import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.LocaleListCompat +import de.rwth_aachen.phyphox.common.AppDelegate import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.usecase.language.ObserveCurrentAppLanguageUseCase import kotlinx.coroutines.CoroutineScope @@ -9,20 +10,21 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import javax.inject.Inject - -class LanguageDelegate @Inject constructor( +class AppLanguageDelegate @Inject constructor( private val observeCurrentAppLanguageUseCase: ObserveCurrentAppLanguageUseCase, -) { - fun start(scope: CoroutineScope) { +) : AppDelegate { + override fun start(coroutineScope: CoroutineScope) { observeCurrentAppLanguageUseCase() .onEach { appLanguage -> val localeList = - if (appLanguage.identifier == AppLanguage.SYSTEM_DEFAULT_IDENTIFIER) { // Use system default if the code is empty + if (appLanguage.identifier == AppLanguage.SYSTEM_DEFAULT_IDENTIFIER) { LocaleListCompat.getEmptyLocaleList() } else { LocaleListCompat.forLanguageTags(appLanguage.identifier) } AppCompatDelegate.setApplicationLocales(localeList) - }.launchIn(scope) + }.launchIn(coroutineScope) } + + override fun stop() = Unit } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index a9dcfd78..67536a3d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -14,12 +14,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint import de.rwth_aachen.phyphox.features.settings.presentation.compose.SettingsRoot import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewModel +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsViewmodelViewModel import de.rwth_aachen.phyphox.ui.theme.PhyphoxTheme @AndroidEntryPoint class NewSettingsActivity : ComponentActivity() { - private val viewModel: SettingsViewModel by viewModels() + private val viewModel: SettingsViewmodelViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt similarity index 81% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt index 560d18f8..904e2316 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt @@ -6,11 +6,11 @@ import dagger.hilt.android.lifecycle.HiltViewModel import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.NavigateBack import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.OpenWebpageFromResourceID -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockViewmodelDelegate import de.rwth_aachen.phyphox.utils.UIEventFlow import de.rwth_aachen.phyphox.utils.asFlow import kotlinx.coroutines.CoroutineScope @@ -21,18 +21,18 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -internal class SettingsViewModel @Inject constructor( - private val accessPortDelegate: AccessPortDelegate, - private val appLanguageDelegate: AppLanguageDelegate, - private val appUiModeDelegate: AppUiModeDelegate, - private val graphSizeDelegate: GraphSizeDelegate, - private val proximityLockDelegate: ProximityLockDelegate, +internal class SettingsViewmodelViewModel @Inject constructor( + private val accessPortDelegate: AccessPortViewmodelDelegate, + private val appLanguageDelegate: AppLanguageViewmodelDelegate, + private val appUiModeDelegate: AppUiModeViewmodelDelegate, + private val graphSizeDelegate: GraphSizeViewmodelDelegate, + private val proximityLockDelegate: ProximityLockViewmodelDelegate, ) : ViewModel(), - AccessPortDelegate by accessPortDelegate, - AppLanguageDelegate by appLanguageDelegate, - AppUiModeDelegate by appUiModeDelegate, - GraphSizeDelegate by graphSizeDelegate, - ProximityLockDelegate by proximityLockDelegate { + AccessPortViewmodelDelegate by accessPortDelegate, + AppLanguageViewmodelDelegate by appLanguageDelegate, + AppUiModeViewmodelDelegate by appUiModeDelegate, + GraphSizeViewmodelDelegate by graphSizeDelegate, + ProximityLockViewmodelDelegate by proximityLockDelegate { private val _uiEvent = UIEventFlow() diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsViewmodelDelegate.kt similarity index 51% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsViewmodelDelegate.kt index b9e151eb..46acb2c6 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/SettingsViewmodelDelegate.kt @@ -1,9 +1,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -interface SettingsDelegate { +interface SettingsViewmodelDelegate { fun start(scope: CoroutineScope) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortViewmodelDelegate.kt similarity index 82% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortViewmodelDelegate.kt index 6acb1544..5e8dafcc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/AccessPortViewmodelDelegate.kt @@ -1,11 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow -interface AccessPortDelegate : SettingsDelegate { +interface AccessPortViewmodelDelegate : SettingsViewmodelDelegate { val accessPortFlow: Flow> val portInputModal: Flow diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortViewmodelDelegate.kt similarity index 94% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortViewmodelDelegate.kt index 98c81ffd..9a3927f2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/accessport/DefaultAccessPortViewmodelDelegate.kt @@ -3,7 +3,6 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.GetAccessPortRangeApplicationService import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.ObserveCurrentAccessPortUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.accessport.UpdateAccessPortUseCase -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsSheetUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.ui.string.toStringUIModel @@ -19,12 +18,12 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -internal class DefaultAccessPortDelegate @Inject constructor( +internal class DefaultAccessPortViewmodelDelegate @Inject constructor( private val observeCurrentAccessPort: ObserveCurrentAccessPortUseCase, private val getAccessPortRange: GetAccessPortRangeApplicationService, private val updateAccessPort: UpdateAccessPortUseCase, private val uiBuilder: UiBuilder, -) : AccessPortDelegate { +) : AccessPortViewmodelDelegate { private val currentAccessPortStateFlow = MutableStateFlow>(UiResourceState.Loading) override val accessPortFlow: Flow> = currentAccessPortStateFlow.map { accessPort -> diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageViewmodelDelegate.kt similarity index 81% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageViewmodelDelegate.kt index 534ebc24..82ddb554 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/AppLanguageViewmodelDelegate.kt @@ -1,10 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow -interface AppLanguageDelegate : SettingsDelegate { +interface AppLanguageViewmodelDelegate : SettingsViewmodelDelegate { val currentAppLanguageFlow: Flow> diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageViewmodelDelegate.kt similarity index 96% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageViewmodelDelegate.kt index 1a50bc29..2a6651d9 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/applanguage/DefaultAppLanguageViewmodelDelegate.kt @@ -17,12 +17,12 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import javax.inject.Inject -internal class DefaultAppLanguageDelegate @Inject constructor( +internal class DefaultAppLanguageViewmodelDelegate @Inject constructor( private val observeCurrentAppLanguage: ObserveCurrentAppLanguageUseCase, private val getSupportedLanguages: GetSupportedLanguagesUseCase, private val updateAppLanguage: UpdateAppLanguageUseCase, private val uiBuilder: UiBuilder, -) : AppLanguageDelegate { +) : AppLanguageViewmodelDelegate { private val currentLanguageFlow = MutableStateFlow>(UiResourceState.Loading) override val currentAppLanguageFlow: Flow> = currentLanguageFlow.map { appLanguage -> diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt similarity index 83% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt index 3e633c08..3f12f93f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt @@ -2,12 +2,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface AppUiModeDelegate : SettingsDelegate { +interface AppUiModeViewmodelDelegate : SettingsViewmodelDelegate { val appUiModeFlow: Flow>>> suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt similarity index 96% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt index a1ac12ae..0b98dee2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt @@ -17,12 +17,12 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import javax.inject.Inject -internal class DefaultAppUiModeDelegate @Inject constructor( +internal class DefaultAppUiModeViewmodelDelegate @Inject constructor( private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, private val getSupportedUiModes: GetSupportedAppUiModeUseCase, private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase, private val uiBuilder: UiBuilder, -) : AppUiModeDelegate { +) : AppUiModeViewmodelDelegate { private val currentUiModeUiModelFlow = MutableStateFlow>(UiResourceState.Loading) private val supportedUiModesFlowUiModel = MutableStateFlow>>(UiResourceState.Loading) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt similarity index 95% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt index 5ff78a85..bef63ae5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt @@ -16,12 +16,12 @@ import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import javax.inject.Inject -internal class DefaultGraphSizeDelegate @Inject constructor( +internal class DefaultGraphSizeViewmodelDelegate @Inject constructor( private val observeCurrentGraphSize: ObserveCurrentGraphSizeUseCase, private val getGraphSizeRange: GetGraphSizeRangeUseCase, private val updateGraphSize: UpdateGraphSizeUseCase, private val uiBuilder: UiBuilder, -) : GraphSizeDelegate { +) : GraphSizeViewmodelDelegate { private val currentGraphSizeFlow = MutableStateFlow>( UiResourceState.Loading, ) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt similarity index 78% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt index 46ec65ba..eed227d7 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt @@ -1,12 +1,11 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface GraphSizeDelegate : SettingsDelegate{ +interface GraphSizeViewmodelDelegate : SettingsViewmodelDelegate{ val graphSizeFlow: Flow> suspend fun updateGraphSize(size: Float) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockViewmodelDelegate.kt similarity index 93% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockViewmodelDelegate.kt index 3197a1c7..501a4ae0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/DefaultProximityLockViewmodelDelegate.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import javax.inject.Inject -class DefaultProximityLockDelegate @Inject constructor( +class DefaultProximityLockViewmodelDelegate @Inject constructor( private val observeProximityLockEnabled: ObserveIsCurrentProximityLockEnabledUseCase, private val updateProximityLockEnabled: UpdateProximityLockStatusUseCase, -) : ProximityLockDelegate { +) : ProximityLockViewmodelDelegate { private val proximityLockEnabledFlow = MutableStateFlow>(UiResourceState.Loading) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockViewmodelDelegate.kt similarity index 74% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockViewmodelDelegate.kt index 769c68ef..775d29c0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/proximitylock/ProximityLockViewmodelDelegate.kt @@ -1,11 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -interface ProximityLockDelegate: SettingsDelegate { +interface ProximityLockViewmodelDelegate: SettingsViewmodelDelegate { val proximityLockFlow: Flow> suspend fun updateProximityLockStatus(enabled: Boolean) From b6f039c57dbfb455199b3947fabfa39a0e06ee5e Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 21:27:32 +0100 Subject: [PATCH 109/111] change package structure --- .../ClickablePreferenceItem.kt | 6 +++--- .../ClickablePreferenceItemPreviewProvider.kt | 2 +- .../LanguagePreferenceItem.kt | 6 +++--- .../PreferenceCategoryHeader.kt | 2 +- .../PreferenceCategoryHeaderPreviewProvider.kt | 2 +- .../preferenceitem/PreferenceItem.kt | 2 +- .../PreferenceSummaryItem.kt | 2 +- .../seekbarpreferenceitem/SeekBarConfig.kt | 2 +- .../SeekBarPreferenceItem.kt | 6 +++--- .../SeekBarPreferenceItemPreviewProvider.kt | 2 +- .../SegmentedButtonPreferenceItem.kt | 6 +++--- .../SegmentedButtonUiModel.kt | 2 +- .../SwitchPreferenceItem.kt | 6 +++--- .../SwitchPreferenceItemPreviewProvider.kt | 2 +- .../compose/settingscontent/SettingsContent.kt | 17 ++++++++--------- .../presentation/viewmodel/SettingsAction.kt | 2 +- .../presentation/viewmodel/SettingsUiState.kt | 5 ++--- .../presentation/viewmodel/UiBuilder.kt | 4 ++-- .../appuimode/AppUiModeViewmodelDelegate.kt | 2 +- .../DefaultAppUiModeViewmodelDelegate.kt | 2 +- .../DefaultGraphSizeViewmodelDelegate.kt | 2 +- .../graphsize/GraphSizeViewmodelDelegate.kt | 2 +- 22 files changed, 41 insertions(+), 43 deletions(-) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/clickablepreferenceitem/ClickablePreferenceItem.kt (94%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt (94%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/clickablepreferenceitem/LanguagePreferenceItem.kt (92%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/preferencecategoryheader/PreferenceCategoryHeader.kt (97%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt (92%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/preferenceitem/PreferenceItem.kt (99%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/preferencesummaryitem/PreferenceSummaryItem.kt (97%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/seekbarpreferenceitem/SeekBarConfig.kt (91%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/seekbarpreferenceitem/SeekBarPreferenceItem.kt (95%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt (94%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt (96%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt (85%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/switchpreferenceitem/SwitchPreferenceItem.kt (95%) rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/{ => common}/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt (93%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItem.kt similarity index 94% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItem.kt index 04d95de9..ee30abfb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.clickablepreferenceitem import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -11,8 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt similarity index 94% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt index 80e2e3de..d892d13b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/ClickablePreferenceItemPreviewProvider.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.clickablepreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/LanguagePreferenceItem.kt similarity index 92% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/LanguagePreferenceItem.kt index ba6784e5..be1ad8e0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/clickablepreferenceitem/LanguagePreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/clickablepreferenceitem/LanguagePreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.clickablepreferenceitem import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -8,8 +8,8 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeader.kt similarity index 97% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeader.kt index f16c1494..0798c27f 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeader.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeader.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencecategoryheader import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.MaterialTheme diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt similarity index 92% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt index d27992cb..60dcfac1 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencecategoryheader/PreferenceCategoryHeaderPreviewProvider.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencecategoryheader import androidx.compose.ui.tooling.preview.PreviewParameterProvider import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferenceitem/PreferenceItem.kt similarity index 99% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferenceitem/PreferenceItem.kt index d5c8a221..1667b939 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferenceitem/PreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferenceitem/PreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencesummaryitem/PreferenceSummaryItem.kt similarity index 97% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencesummaryitem/PreferenceSummaryItem.kt index e136759c..89bf70a8 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/preferencesummaryitem/PreferenceSummaryItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/preferencesummaryitem/PreferenceSummaryItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarConfig.kt similarity index 91% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarConfig.kt index 64bb4e53..ab3bd2d0 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarConfig.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarConfig.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem data class SeekBarConfig( val currentSize: Float, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItem.kt similarity index 95% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItem.kt index a58e9bec..eed93c88 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth @@ -11,8 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt similarity index 94% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt index b6aa9414..853e5108 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/seekbarpreferenceitem/SeekBarPreferenceItemPreviewProvider.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider import de.rwth_aachen.phyphox.utils.UiResourceState diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt similarity index 96% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index f01df62b..16ba5b5a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth @@ -14,8 +14,8 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt similarity index 85% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt index a7a9b0e8..0d3dbafe 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonUiModel.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItem.kt similarity index 95% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItem.kt index 7ccad54a..004a241c 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItem.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.switchpreferenceitem import android.content.res.Configuration import androidx.compose.foundation.clickable @@ -14,8 +14,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferenceitem.PreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencesummaryitem.PreferenceSummaryItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferenceitem.PreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencesummaryitem.PreferenceSummaryItem import de.rwth_aachen.phyphox.ui.skeleton import de.rwth_aachen.phyphox.ui.string.LoremIpsumStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt similarity index 93% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt index b4a61f2e..188526b4 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/switchpreferenceitem/SwitchPreferenceItemPreviewProvider.kt @@ -1,4 +1,4 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem +package de.rwth_aachen.phyphox.features.settings.presentation.compose.common.switchpreferenceitem import androidx.compose.ui.tooling.preview.PreviewParameterProvider import de.rwth_aachen.phyphox.utils.UiResourceState diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt index b443741c..584e3bcb 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/settingscontent/SettingsContent.kt @@ -21,16 +21,15 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.ClickablePreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.clickablepreferenceitem.LanguagePreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.preferencecategoryheader.PreferenceCategoryHeader -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarPreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonPreferenceItem -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel -import de.rwth_aachen.phyphox.features.settings.presentation.compose.switchpreferenceitem.SwitchPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.clickablepreferenceitem.ClickablePreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.clickablepreferenceitem.LanguagePreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.preferencecategoryheader.PreferenceCategoryHeader +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonPreferenceItem +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.switchpreferenceitem.SwitchPreferenceItem import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsAction -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt index 08f3ebb6..d486acaa 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -1,7 +1,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel sealed interface SettingsAction { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt index 53457835..cd67dd59 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsUiState.kt @@ -1,9 +1,8 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortUiState +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.StringUIModel import de.rwth_aachen.phyphox.utils.UiResourceState diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt index e0169e12..8aecbd9e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/UiBuilder.kt @@ -4,8 +4,8 @@ import de.rwth_aachen.phyphox.R import de.rwth_aachen.phyphox.features.settings.domain.model.AppLanguage import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.domain.model.errors.AccessPortOutOfRange -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.LanguageUiModel import de.rwth_aachen.phyphox.ui.string.ResourceStringUIModel import de.rwth_aachen.phyphox.ui.string.StringUIModel diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt index 3f12f93f..541b684d 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt @@ -1,7 +1,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt index 0b98dee2..2de97660 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt @@ -4,7 +4,7 @@ import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.UpdateCurrentAppUiModeUseCase -import de.rwth_aachen.phyphox.features.settings.presentation.compose.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt index bef63ae5..e25e8606 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/DefaultGraphSizeViewmodelDelegate.kt @@ -3,7 +3,7 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegate import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.GetGraphSizeRangeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.ObserveCurrentGraphSizeUseCase import de.rwth_aachen.phyphox.features.settings.domain.usecase.graphsize.UpdateGraphSizeUseCase -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.CoroutineScope diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt index eed227d7..bffef838 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/graphsize/GraphSizeViewmodelDelegate.kt @@ -1,6 +1,6 @@ package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize -import de.rwth_aachen.phyphox.features.settings.presentation.compose.seekbarpreferenceitem.SeekBarConfig +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.seekbarpreferenceitem.SeekBarConfig import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.SettingsViewmodelDelegate import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow From da83c6d77e2f055b50a78547137cd5ed92b394cc Mon Sep 17 00:00:00 2001 From: owl Date: Sun, 11 Jan 2026 21:31:58 +0100 Subject: [PATCH 110/111] update uimode event --- .../usecase/uimode/UpdateCurrentAppUiModeUseCase.kt | 13 +++++++++++-- .../SegmentedButtonPreferenceItem.kt | 4 ++-- .../presentation/viewmodel/SettingsAction.kt | 2 +- .../appuimode/AppUiModeViewmodelDelegate.kt | 2 +- .../appuimode/DefaultAppUiModeViewmodelDelegate.kt | 4 ++-- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt index 9b6917c1..92863161 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/UpdateCurrentAppUiModeUseCase.kt @@ -1,9 +1,18 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode import javax.inject.Inject class UpdateCurrentAppUiModeUseCase @Inject constructor( - private val repository: AppPreferencesRepository -){ + private val repository: AppPreferencesRepository, +) { + suspend operator fun invoke(appUiMode: AppUiMode): Result { + return try { + repository.updateAppUiMode(appUiMode) + Result.success(Unit) + } catch (e: Throwable) { + Result.failure(e) + } + } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt index 16ba5b5a..b28ee2e5 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/compose/common/segmentedbuttonpreferenceitem/SegmentedButtonPreferenceItem.kt @@ -30,7 +30,7 @@ fun SegmentedButtonPreferenceItem( summary: StringUIModel? = null, iconRes: Int? = null, config: UiResourceState>>, - onOptionSelected: (SegmentedButtonUiModel) -> Unit, + onOptionSelected: (AppUiMode) -> Unit, ) { PreferenceItem( modifier = modifier, @@ -59,7 +59,7 @@ fun SegmentedButtonPreferenceItem( count = config.data.size, ), onClick = { - onOptionSelected(option) + onOptionSelected(option.item) }, selected = option.isSelected, label = { Text(option.text.resolve()) }, diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt index d486acaa..2d51669e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsAction.kt @@ -5,7 +5,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segm sealed interface SettingsAction { - data class OnUiModeItemSelected(val appUiMode: SegmentedButtonUiModel) : SettingsAction + data class OnUiModeItemSelected(val appUiMode: AppUiMode) : SettingsAction data class OnGraphSizeChanged(val size: Float) : SettingsAction data class OnProximityLockChanged(val enabled: Boolean) : SettingsAction data class OnAccessPortChanged(val newPort: String) : SettingsAction diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt index 541b684d..1ea82cef 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt @@ -9,5 +9,5 @@ import kotlinx.coroutines.flow.Flow interface AppUiModeViewmodelDelegate : SettingsViewmodelDelegate { val appUiModeFlow: Flow>>> - suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) + suspend fun updateAppUiMode(appUiMode: AppUiMode) } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt index 2de97660..241819e6 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt @@ -37,8 +37,8 @@ internal class DefaultAppUiModeViewmodelDelegate @Inject constructor( fetchSupportedUiModes(scope) } - override suspend fun updateAppUiMode(appUiMode: SegmentedButtonUiModel) { - + override suspend fun updateAppUiMode(appUiMode: AppUiMode) { + updateCurrentAppUiMode(appUiMode) } private fun fetchSupportedUiModes(scope: CoroutineScope) { From 0fbf6bf18545125c9afd11cf5daa67d1bd27cc7e Mon Sep 17 00:00:00 2001 From: owl Date: Fri, 30 Jan 2026 21:59:13 +0100 Subject: [PATCH 111/111] remove everything regarding the app uimode --- .../main/java/de/rwth_aachen/phyphox/App.kt | 1 + .../phyphox/di/ApplicationScope.kt | 7 +++ .../phyphox/di/CoroutineScopesModule.kt | 21 +++++++ .../features/settings/di/SettingsModule.kt | 9 ++- .../uimode/GetSupportedAppUiModeUseCase.kt | 9 +-- .../uimode/ObserveCurrentAppUiModeUseCase.kt | 21 +++++-- .../presentation/NewSettingsActivity.kt | 4 +- .../presentation/viewmodel/SettingsEvent.kt | 1 + .../viewmodel/SettingsViewmodelViewModel.kt | 6 +- .../DefaultAppUiModeViewmodelDelegate.kt | 60 ------------------- .../DefaultUiModeViewmodelDelegate.kt | 21 +++++++ ...Delegate.kt => UiModeViewmodelDelegate.kt} | 2 +- .../de/rwth_aachen/phyphox/ui/theme/Theme.kt | 1 + 13 files changed, 81 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/di/ApplicationScope.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/di/CoroutineScopesModule.kt delete mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt create mode 100644 app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultUiModeViewmodelDelegate.kt rename app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/{AppUiModeViewmodelDelegate.kt => UiModeViewmodelDelegate.kt} (90%) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/App.kt b/app/src/main/java/de/rwth_aachen/phyphox/App.kt index 8d678c60..187fe3d2 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/App.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/App.kt @@ -24,6 +24,7 @@ class App : MultiDexApplication() { override fun onCreate() { super.onCreate() + appDelegates.forEach { it.start(applicationScope) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/di/ApplicationScope.kt b/app/src/main/java/de/rwth_aachen/phyphox/di/ApplicationScope.kt new file mode 100644 index 00000000..8289f3fd --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/di/ApplicationScope.kt @@ -0,0 +1,7 @@ +package de.rwth_aachen.phyphox.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class ApplicationScope diff --git a/app/src/main/java/de/rwth_aachen/phyphox/di/CoroutineScopesModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/di/CoroutineScopesModule.kt new file mode 100644 index 00000000..72c83990 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/di/CoroutineScopesModule.kt @@ -0,0 +1,21 @@ +package de.rwth_aachen.phyphox.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object CoroutineScopesModule { + + @Provides + @Singleton + @ApplicationScope + fun provideApplicationScope(): CoroutineScope = + CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) +} diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt index 4ab1fc0d..0f01f57b 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/di/SettingsModule.kt @@ -22,13 +22,12 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.DefaultAccessPortViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.DefaultAppLanguageViewmodelDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeViewmodelDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.DefaultAppUiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.UiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.DefaultUiModeViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.DefaultGraphSizeViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.DefaultProximityLockViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockViewmodelDelegate -import kotlinx.coroutines.CoroutineScope import javax.inject.Qualifier import javax.inject.Singleton @@ -48,8 +47,8 @@ abstract class SettingsModule { @Binds internal abstract fun bindsAppUiModeDelegate( - implementation: DefaultAppUiModeViewmodelDelegate, - ): AppUiModeViewmodelDelegate + implementation: DefaultUiModeViewmodelDelegate, + ): UiModeViewmodelDelegate @Binds internal abstract fun bindsGraphSizeDelegate( diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt index 0de4f170..48232750 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/GetSupportedAppUiModeUseCase.kt @@ -1,15 +1,10 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode -import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import kotlinx.coroutines.delay import javax.inject.Inject -class GetSupportedAppUiModeUseCase @Inject constructor( - private val repository: AppPreferencesRepository -) { - suspend operator fun invoke(): List { - delay(2500) +class GetSupportedAppUiModeUseCase @Inject constructor() { + operator fun invoke(): List { return AppUiMode.entries } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt index ac5da30b..aee2b1cc 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/domain/usecase/uimode/ObserveCurrentAppUiModeUseCase.kt @@ -1,15 +1,26 @@ package de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode +import de.rwth_aachen.phyphox.di.ApplicationScope import de.rwth_aachen.phyphox.features.settings.domain.data.AppPreferencesRepository import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import javax.inject.Inject + class ObserveCurrentAppUiModeUseCase @Inject constructor( - private val repository: AppPreferencesRepository + private val repository: AppPreferencesRepository, + @ApplicationScope private val appScope: CoroutineScope, ) { - operator fun invoke(): Flow { - return flowOf(AppUiMode.LIGHT) + operator fun invoke(): StateFlow { + return repository.observeAppUiMode().map { it ?: AppUiMode.SYSTEM }.distinctUntilChanged().stateIn( + scope = appScope, + started = SharingStarted.Eagerly, + initialValue = AppUiMode.SYSTEM, + ) } } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt index 67536a3d..d93278dd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/NewSettingsActivity.kt @@ -41,7 +41,7 @@ class NewSettingsActivity : ComponentActivity() { @Composable - private fun ObserveSettingsEvents(){ + private fun ObserveSettingsEvents() { val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher LaunchedEffect(Unit) { viewModel.uiEvent.collect { event -> @@ -55,4 +55,6 @@ class NewSettingsActivity : ComponentActivity() { } + } + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt index f4ef5ba2..501978bd 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsEvent.kt @@ -5,5 +5,6 @@ import androidx.annotation.StringRes sealed interface SettingsEvent { data class OpenWebpage(val url: String) : SettingsEvent data class OpenWebpageFromResourceID(@param:StringRes val urlResourceId: Int) : SettingsEvent + data object NavigateBack : SettingsEvent } diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt index 904e2316..be0c8d0a 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/SettingsViewmodelViewModel.kt @@ -8,7 +8,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsE import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.SettingsEvent.OpenWebpageFromResourceID import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.accessport.AccessPortViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.applanguage.AppLanguageViewmodelDelegate -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.AppUiModeViewmodelDelegate +import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode.UiModeViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.graphsize.GraphSizeViewmodelDelegate import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.proximitylock.ProximityLockViewmodelDelegate import de.rwth_aachen.phyphox.utils.UIEventFlow @@ -24,13 +24,13 @@ import javax.inject.Inject internal class SettingsViewmodelViewModel @Inject constructor( private val accessPortDelegate: AccessPortViewmodelDelegate, private val appLanguageDelegate: AppLanguageViewmodelDelegate, - private val appUiModeDelegate: AppUiModeViewmodelDelegate, + private val appUiModeDelegate: UiModeViewmodelDelegate, private val graphSizeDelegate: GraphSizeViewmodelDelegate, private val proximityLockDelegate: ProximityLockViewmodelDelegate, ) : ViewModel(), AccessPortViewmodelDelegate by accessPortDelegate, AppLanguageViewmodelDelegate by appLanguageDelegate, - AppUiModeViewmodelDelegate by appUiModeDelegate, + UiModeViewmodelDelegate by appUiModeDelegate, GraphSizeViewmodelDelegate by graphSizeDelegate, ProximityLockViewmodelDelegate by proximityLockDelegate { diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt deleted file mode 100644 index 241819e6..00000000 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultAppUiModeViewmodelDelegate.kt +++ /dev/null @@ -1,60 +0,0 @@ -package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode - -import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode -import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.GetSupportedAppUiModeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.ObserveCurrentAppUiModeUseCase -import de.rwth_aachen.phyphox.features.settings.domain.usecase.uimode.UpdateCurrentAppUiModeUseCase -import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel -import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.UiBuilder -import de.rwth_aachen.phyphox.utils.UiResourceState -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.launch -import javax.inject.Inject - -internal class DefaultAppUiModeViewmodelDelegate @Inject constructor( - private val observeCurrentUiMode: ObserveCurrentAppUiModeUseCase, - private val getSupportedUiModes: GetSupportedAppUiModeUseCase, - private val updateCurrentAppUiMode: UpdateCurrentAppUiModeUseCase, - private val uiBuilder: UiBuilder, -) : AppUiModeViewmodelDelegate { - private val currentUiModeUiModelFlow = MutableStateFlow>(UiResourceState.Loading) - private val supportedUiModesFlowUiModel = - MutableStateFlow>>(UiResourceState.Loading) - - override val appUiModeFlow: Flow>>> = - combine(currentUiModeUiModelFlow, supportedUiModesFlowUiModel) { current, modes -> - uiBuilder.buildAppUiModeResource(current, modes) - } - - override fun start(scope: CoroutineScope) { - observeAppUiMode(scope) - fetchSupportedUiModes(scope) - } - - override suspend fun updateAppUiMode(appUiMode: AppUiMode) { - updateCurrentAppUiMode(appUiMode) - } - - private fun fetchSupportedUiModes(scope: CoroutineScope) { - scope.launch { - supportedUiModesFlowUiModel.value = UiResourceState.Success( - getSupportedUiModes() - ) - } - } - - private fun observeAppUiMode(scope: CoroutineScope){ - observeCurrentUiMode().onStart { - currentUiModeUiModelFlow.value = UiResourceState.Loading - }.onEach { - currentUiModeUiModelFlow.value = UiResourceState.Success(it) - }.launchIn(scope) - } -} - diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultUiModeViewmodelDelegate.kt new file mode 100644 index 00000000..f2305ce5 --- /dev/null +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/DefaultUiModeViewmodelDelegate.kt @@ -0,0 +1,21 @@ +package de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates.appuimode + +import de.rwth_aachen.phyphox.features.settings.domain.model.AppUiMode +import de.rwth_aachen.phyphox.features.settings.presentation.compose.common.segmentedbuttonpreferenceitem.SegmentedButtonUiModel +import de.rwth_aachen.phyphox.utils.UiResourceState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject + +internal class DefaultUiModeViewmodelDelegate @Inject constructor() : UiModeViewmodelDelegate { + + override val appUiModeFlow: Flow>>> = + flowOf(UiResourceState.Loading) + + + override fun start(scope: CoroutineScope) {} + + override suspend fun updateAppUiMode(appUiMode: AppUiMode) {} +} + diff --git a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/UiModeViewmodelDelegate.kt similarity index 90% rename from app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt rename to app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/UiModeViewmodelDelegate.kt index 1ea82cef..0a58476e 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/AppUiModeViewmodelDelegate.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/features/settings/presentation/viewmodel/delegates/appuimode/UiModeViewmodelDelegate.kt @@ -6,7 +6,7 @@ import de.rwth_aachen.phyphox.features.settings.presentation.viewmodel.delegates import de.rwth_aachen.phyphox.utils.UiResourceState import kotlinx.coroutines.flow.Flow -interface AppUiModeViewmodelDelegate : SettingsViewmodelDelegate { +interface UiModeViewmodelDelegate : SettingsViewmodelDelegate { val appUiModeFlow: Flow>>> suspend fun updateAppUiMode(appUiMode: AppUiMode) diff --git a/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt b/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt index e102b1f3..3b02e386 100644 --- a/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt +++ b/app/src/main/java/de/rwth_aachen/phyphox/ui/theme/Theme.kt @@ -2,6 +2,7 @@ package de.rwth_aachen.phyphox.ui.theme import android.app.Activity import android.os.Build +import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme