From cef7b32794311fcd2899833620389ee17d81bc88 Mon Sep 17 00:00:00 2001 From: Norbel AMBANUMBEN Date: Fri, 16 Aug 2024 14:36:17 +0100 Subject: [PATCH] bootstrap settings --- .../composeResources/drawable/.gitignore | 2 +- .../drawable/category_aldr.xml | 25 + .../drawable/category_anon.xml | 35 + .../drawable/category_comm.xml | 15 + .../drawable/category_comt.xml | 20 + .../drawable/category_ctrl.xml | 55 ++ .../drawable/category_cultr.xml | 15 + .../drawable/category_date.xml | 15 + .../drawable/category_econ.xml | 30 + .../drawable/category_env.xml | 20 + .../drawable/category_file.xml | 20 + .../drawable/category_game.xml | 15 + .../drawable/category_gmb.xml | 28 + .../drawable/category_govt.xml | 25 + .../drawable/category_grp.xml | 25 + .../drawable/category_hack.xml | 15 + .../drawable/category_hate.xml | 20 + .../drawable/category_host.xml | 15 + .../drawable/category_humr.xml | 15 + .../drawable/category_igo.xml | 25 + .../drawable/category_lgbt.xml | 15 + .../drawable/category_milx.xml | 15 + .../drawable/category_misc.xml | 15 + .../drawable/category_mmed.xml | 25 + .../drawable/category_news.xml | 35 + .../drawable/category_polr.xml | 15 + .../drawable/category_porn.xml | 35 + .../drawable/category_prov.xml | 15 + .../drawable/category_pubh.xml | 20 + .../drawable/category_rel.xml | 20 + .../drawable/category_srch.xml | 20 + .../drawable/category_xed.xml | 20 + .../values/strings-common.xml | 69 ++ .../composeResources/values/untraslatable.xml | 69 -- .../data/repositories/PreferenceRepository.kt | 14 + .../kotlin/org/ooni/probe/di/Dependencies.kt | 8 +- .../ooni/probe/ui/navigation/Navigation.kt | 18 +- .../org/ooni/probe/ui/navigation/Screen.kt | 3 +- .../ooni/probe/ui/settings/SettingsScreen.kt | 637 +++++++++++++++--- .../probe/ui/settings/SettingsViewModel.kt | 5 +- .../category/SettingsCategoryScreen.kt | 333 ++++----- .../category/SettingsCategoryViewModel.kt | 52 +- .../repositories/PreferenceRepositoryTest.kt | 103 +-- 43 files changed, 1540 insertions(+), 456 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_aldr.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_anon.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_comm.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_comt.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_ctrl.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_cultr.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_date.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_econ.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_env.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_file.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_game.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_gmb.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_govt.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_grp.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_hack.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_hate.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_host.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_humr.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_igo.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_lgbt.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_milx.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_misc.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_mmed.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_news.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_polr.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_porn.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_prov.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_pubh.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_rel.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_srch.xml create mode 100644 composeApp/src/commonMain/composeResources/drawable/category_xed.xml delete mode 100644 composeApp/src/commonMain/composeResources/values/untraslatable.xml diff --git a/composeApp/src/commonMain/composeResources/drawable/.gitignore b/composeApp/src/commonMain/composeResources/drawable/.gitignore index f9d23428..15c3e694 100644 --- a/composeApp/src/commonMain/composeResources/drawable/.gitignore +++ b/composeApp/src/commonMain/composeResources/drawable/.gitignore @@ -1,7 +1,7 @@ -logo.xml test_experimental.xml test_performance.xml test_instant_messaging.xml test_websites.xml +logo.xml test_circumvention.xml \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/category_aldr.xml b/composeApp/src/commonMain/composeResources/drawable/category_aldr.xml new file mode 100644 index 00000000..4adffcd8 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_aldr.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_anon.xml b/composeApp/src/commonMain/composeResources/drawable/category_anon.xml new file mode 100644 index 00000000..da39c354 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_anon.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_comm.xml b/composeApp/src/commonMain/composeResources/drawable/category_comm.xml new file mode 100644 index 00000000..83be7c22 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_comm.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_comt.xml b/composeApp/src/commonMain/composeResources/drawable/category_comt.xml new file mode 100644 index 00000000..36114747 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_comt.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_ctrl.xml b/composeApp/src/commonMain/composeResources/drawable/category_ctrl.xml new file mode 100644 index 00000000..185d2c48 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_ctrl.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_cultr.xml b/composeApp/src/commonMain/composeResources/drawable/category_cultr.xml new file mode 100644 index 00000000..1c582a44 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_cultr.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_date.xml b/composeApp/src/commonMain/composeResources/drawable/category_date.xml new file mode 100644 index 00000000..b75aec46 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_date.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_econ.xml b/composeApp/src/commonMain/composeResources/drawable/category_econ.xml new file mode 100644 index 00000000..ce617743 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_econ.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_env.xml b/composeApp/src/commonMain/composeResources/drawable/category_env.xml new file mode 100644 index 00000000..eef29942 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_env.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_file.xml b/composeApp/src/commonMain/composeResources/drawable/category_file.xml new file mode 100644 index 00000000..ce7ca471 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_file.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_game.xml b/composeApp/src/commonMain/composeResources/drawable/category_game.xml new file mode 100644 index 00000000..03409645 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_game.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_gmb.xml b/composeApp/src/commonMain/composeResources/drawable/category_gmb.xml new file mode 100644 index 00000000..24402937 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_gmb.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_govt.xml b/composeApp/src/commonMain/composeResources/drawable/category_govt.xml new file mode 100644 index 00000000..7e99f99a --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_govt.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_grp.xml b/composeApp/src/commonMain/composeResources/drawable/category_grp.xml new file mode 100644 index 00000000..a3276245 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_grp.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_hack.xml b/composeApp/src/commonMain/composeResources/drawable/category_hack.xml new file mode 100644 index 00000000..1ce1c6a7 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_hack.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_hate.xml b/composeApp/src/commonMain/composeResources/drawable/category_hate.xml new file mode 100644 index 00000000..57c370c0 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_hate.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_host.xml b/composeApp/src/commonMain/composeResources/drawable/category_host.xml new file mode 100644 index 00000000..638b1e56 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_host.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_humr.xml b/composeApp/src/commonMain/composeResources/drawable/category_humr.xml new file mode 100644 index 00000000..3497c37a --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_humr.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_igo.xml b/composeApp/src/commonMain/composeResources/drawable/category_igo.xml new file mode 100644 index 00000000..b2bd3d9f --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_igo.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_lgbt.xml b/composeApp/src/commonMain/composeResources/drawable/category_lgbt.xml new file mode 100644 index 00000000..890ad7cc --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_lgbt.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_milx.xml b/composeApp/src/commonMain/composeResources/drawable/category_milx.xml new file mode 100644 index 00000000..18d41657 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_milx.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_misc.xml b/composeApp/src/commonMain/composeResources/drawable/category_misc.xml new file mode 100644 index 00000000..11d3dd82 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_misc.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_mmed.xml b/composeApp/src/commonMain/composeResources/drawable/category_mmed.xml new file mode 100644 index 00000000..078222f1 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_mmed.xml @@ -0,0 +1,25 @@ + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_news.xml b/composeApp/src/commonMain/composeResources/drawable/category_news.xml new file mode 100644 index 00000000..8da1b492 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_news.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_polr.xml b/composeApp/src/commonMain/composeResources/drawable/category_polr.xml new file mode 100644 index 00000000..ea78fddd --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_polr.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_porn.xml b/composeApp/src/commonMain/composeResources/drawable/category_porn.xml new file mode 100644 index 00000000..486cf705 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_porn.xml @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_prov.xml b/composeApp/src/commonMain/composeResources/drawable/category_prov.xml new file mode 100644 index 00000000..6a2063f7 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_prov.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_pubh.xml b/composeApp/src/commonMain/composeResources/drawable/category_pubh.xml new file mode 100644 index 00000000..978227ac --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_pubh.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_rel.xml b/composeApp/src/commonMain/composeResources/drawable/category_rel.xml new file mode 100644 index 00000000..fb82c260 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_rel.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_srch.xml b/composeApp/src/commonMain/composeResources/drawable/category_srch.xml new file mode 100644 index 00000000..3b995649 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_srch.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/drawable/category_xed.xml b/composeApp/src/commonMain/composeResources/drawable/category_xed.xml new file mode 100644 index 00000000..95f53650 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/category_xed.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/composeApp/src/commonMain/composeResources/values/strings-common.xml b/composeApp/src/commonMain/composeResources/values/strings-common.xml index 9280e225..0436d649 100644 --- a/composeApp/src/commonMain/composeResources/values/strings-common.xml +++ b/composeApp/src/commonMain/composeResources/values/strings-common.xml @@ -39,4 +39,73 @@ Advanced Send email to support + Debug logs + See recent logs + Language Setting + Storage usage + Warn when VPN is in use + + Drugs & Alcohol + Religion + Pornography + Provocative Attire + Political Criticism + Human Rights Issues + Environment + Terrorism and Militants + Hate Speech + News Media + Sex Education + Public Health + Gambling + Circumvention tools + Online Dating + Social Networking + LGBTQ+ + File-sharing + Hacking Tools + Communication Tools + Media sharing + Hosting and Blogging + Search Engines + Gaming + Culture + Economics + Government + E-commerce + Control content + Intergovernmental Orgs. + Miscellaneous content + Use and sale of drugs and alcohol + Religious issues, both supportive and critical + Hard-core and soft-core pornography + Provocative attire and portrayal of women wearing minimal clothing + Critical political viewpoints + Human rights issues + Discussions on environmental issues + Terrorism, violent militant or separatist movements + Disparaging of particular groups based on race, sex, sexuality or other characteristics + Major news websites, regional news outlets and independent media + Sexual health issues including contraception, STD\'s, rape prevention and abortion + Public health issues, such as COVID-19, HIV/AIDS, Ebola + Online gambling and betting + Anonymization, censorship circumvention and encryption + Online dating sites + Online social networking tools and platforms + LGBTQ+ communities discussing related issues (excluding pornography) + File sharing including cloud-based file storage, torrents and P2P + Computer security tools and news + Individual and group communication tools including VoIP, messaging and webmail + Video, audio and photo sharing + Web hosting, blogging and other online publishing + Search engines and portals + Online games and gaming platforms (excluding gambling sites) + Entertainment including history, literature, music, film, satire and humour + General economic development and poverty + Government-run websites, including military + Commercial services and products + Benign or innocuous content used for control + Intergovernmental organizations including The United Nations + Sites that haven\'t been categorized yet + diff --git a/composeApp/src/commonMain/composeResources/values/untraslatable.xml b/composeApp/src/commonMain/composeResources/values/untraslatable.xml deleted file mode 100644 index f8bf623d..00000000 --- a/composeApp/src/commonMain/composeResources/values/untraslatable.xml +++ /dev/null @@ -1,69 +0,0 @@ - - notifications - test_options - privacy - proxy - advanced - send_email - about_ooni - - notifications_enabled - notifications_completion - notifications_news - automated_testing_enabled - automated_testing_wifionly - automated_testing_charging - upload_results - debugLogs - storage_usage - send_crash - warn_vpn_in_use - run_http_invalid_request_line - run_http_header_field_manipulation - test_whatsapp - test_telegram - test_facebook_messenger - test_signal - test_psiphon - test_tor - test_riseupvpn - run_ndt - run_dash - experimental - long_running_tests_in_foreground - max_runtime_enabled - max_runtime - - ALDR - REL - PORN - PROV - POLR - HUMR - ENV - MILX - HATE - NEWS - XED - PUBH - GMB - ANON - DATE - GRP - LGBT - FILE - HACK - COMT - MMED - HOST - SRCH - GAME - CULTR - ECON - GOVT - COMM - CTRL - IGO - MISC - - diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/PreferenceRepository.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/PreferenceRepository.kt index 95ed0407..1549e4f5 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/PreferenceRepository.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/data/repositories/PreferenceRepository.kt @@ -61,6 +61,19 @@ class PreferenceRepository( } } +enum class PreferenceCategoryKey(val value: String) { + NOTIFICATIONS("notifications"), + TEST_OPTIONS("test_options"), + PRIVACY("privacy"), + PROXY("proxy"), + ADVANCED("advanced"), + SEND_EMAIL("send_email"), + ABOUT_OONI("about_ooni"), + + WEBSITES_CATEGORIES("websites_categories"), + SEE_RECENT_LOGS("see_recent_logs"), +} + enum class SettingsKey(val value: String) { // Notifications NOTIFICATIONS_ENABLED("notifications_enabled"), @@ -117,6 +130,7 @@ enum class SettingsKey(val value: String) { LANGUAGE_SETTING("language_setting"), DEBUG_LOGS("debugLogs"), WARN_VPN_IN_USE("warn_vpn_in_use"), + STORAGE_SIZE("storage_size"), // purely decorative // MISC DELETE_UPLOADED_JSONS("deleteUploadedJsons"), diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt index b9141ddb..95d71373 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt @@ -13,6 +13,7 @@ import org.ooni.engine.OonimkallBridge import org.ooni.engine.TaskEventMapper import org.ooni.probe.Database import org.ooni.probe.data.models.ResultModel +import org.ooni.probe.data.repositories.PreferenceCategoryKey import org.ooni.probe.data.repositories.PreferenceRepository import org.ooni.probe.data.repositories.ResultRepository import org.ooni.probe.data.repositories.TestDescriptorRepository @@ -26,6 +27,7 @@ import org.ooni.probe.shared.PlatformInfo import org.ooni.probe.ui.dashboard.DashboardViewModel import org.ooni.probe.ui.result.ResultViewModel import org.ooni.probe.ui.results.ResultsViewModel +import org.ooni.probe.ui.settings.SettingsCategoryItem import org.ooni.probe.ui.settings.SettingsViewModel import org.ooni.probe.ui.settings.category.SettingsCategoryViewModel @@ -102,15 +104,17 @@ class Dependencies( fun resultsViewModel(goToResult: (ResultModel.Id) -> Unit) = ResultsViewModel(goToResult, getResults::invoke) - fun settingsViewModel(goToSettingsForCategory: (String) -> Unit) = SettingsViewModel(goToSettingsForCategory) + fun settingsViewModel(goToSettingsForCategory: (PreferenceCategoryKey) -> Unit) = SettingsViewModel(goToSettingsForCategory) fun settingsCategoryViewModel( - goToSettingsForCategory: (String) -> Unit, + goToSettingsForCategory: (PreferenceCategoryKey) -> Unit, onBack: () -> Unit, + category: SettingsCategoryItem, ) = SettingsCategoryViewModel( preferenceManager = preferenceManager, onBack = onBack, goToSettingsForCategory = goToSettingsForCategory, + category = category, ) fun resultViewModel( diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Navigation.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Navigation.kt index 712476a7..4442c8bb 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Navigation.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Navigation.kt @@ -9,14 +9,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import ooniprobe.composeapp.generated.resources.Res -import ooniprobe.composeapp.generated.resources.send_email -import org.jetbrains.compose.resources.stringResource import org.ooni.probe.data.models.ResultModel +import org.ooni.probe.data.repositories.PreferenceCategoryKey import org.ooni.probe.di.Dependencies import org.ooni.probe.ui.dashboard.DashboardScreen import org.ooni.probe.ui.result.ResultScreen import org.ooni.probe.ui.results.ResultsScreen +import org.ooni.probe.ui.settings.SettingsCategoryItem import org.ooni.probe.ui.settings.SettingsScreen import org.ooni.probe.ui.settings.category.SettingsCategoryScreen @@ -81,7 +80,7 @@ fun Navigation( ) { entry -> val category = entry.arguments?.getString("category") ?: return@composable when (category) { - stringResource(Res.string.send_email) -> { + PreferenceCategoryKey.SEND_EMAIL.value -> { // TODO: Implement based on platform } @@ -93,9 +92,18 @@ fun Navigation( navController.navigate(Screen.SettingsCategory(it).route) }, onBack = { navController.navigateUp() }, + category = + SettingsCategoryItem.getSettingsItem( + PreferenceCategoryKey.valueOf(category.uppercase()), + ), ) } - SettingsCategoryScreen(category = category, onEvent = viewModel::onEvent) + val state by viewModel.state.collectAsState() + + SettingsCategoryScreen( + state = state, + onEvent = viewModel::onEvent, + ) } } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Screen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Screen.kt index 38628a63..c113c283 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Screen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/navigation/Screen.kt @@ -3,6 +3,7 @@ package org.ooni.probe.ui.navigation import androidx.navigation.NavType import androidx.navigation.navArgument import org.ooni.probe.data.models.ResultModel +import org.ooni.probe.data.repositories.PreferenceCategoryKey sealed class Screen( val route: String, @@ -22,7 +23,7 @@ sealed class Screen( } } - data class SettingsCategory(val category: String) : Screen("settings/$category") { + data class SettingsCategory(val category: PreferenceCategoryKey) : Screen("settings/${category.value}") { companion object { const val NAV_ROUTE = "settings/{category}" val ARGUMENTS = listOf(navArgument("category") { type = NavType.StringType }) diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsScreen.kt index 1a6fbf8d..ccd76488 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsScreen.kt @@ -11,29 +11,136 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import ooniprobe.composeapp.generated.resources.CategoryCode_ALDR_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_ALDR_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_ANON_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_ANON_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_COMM_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_COMM_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_COMT_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_COMT_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_CTRL_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_CTRL_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_CULTR_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_CULTR_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_DATE_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_DATE_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_ECON_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_ECON_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_ENV_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_ENV_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_FILE_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_FILE_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_GAME_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_GAME_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_GMB_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_GMB_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_GOVT_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_GOVT_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_GRP_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_GRP_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_HACK_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_HACK_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_HATE_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_HATE_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_HOST_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_HOST_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_HUMR_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_HUMR_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_IGO_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_IGO_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_LGBT_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_LGBT_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_MILX_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_MILX_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_MMED_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_MMED_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_NEWS_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_NEWS_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_POLR_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_POLR_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_PORN_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_PORN_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_PROV_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_PROV_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_PUBH_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_PUBH_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_REL_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_REL_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_SRCH_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_SRCH_Name +import ooniprobe.composeapp.generated.resources.CategoryCode_XED_Description +import ooniprobe.composeapp.generated.resources.CategoryCode_XED_Name +import ooniprobe.composeapp.generated.resources.Modal_EnableNotifications_Paragraph import ooniprobe.composeapp.generated.resources.Res import ooniprobe.composeapp.generated.resources.Settings_About_Label +import ooniprobe.composeapp.generated.resources.Settings_Advanced_DebugLogs import ooniprobe.composeapp.generated.resources.Settings_Advanced_Label +import ooniprobe.composeapp.generated.resources.Settings_Advanced_LanguageSettings_Title +import ooniprobe.composeapp.generated.resources.Settings_Advanced_RecentLogs +import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically +import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_ChargingOnly +import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_Footer +import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_WiFiOnly +import ooniprobe.composeapp.generated.resources.Settings_Notifications_Enabled import ooniprobe.composeapp.generated.resources.Settings_Notifications_Label import ooniprobe.composeapp.generated.resources.Settings_Privacy_Label +import ooniprobe.composeapp.generated.resources.Settings_Privacy_SendCrashReports import ooniprobe.composeapp.generated.resources.Settings_Proxy_Label import ooniprobe.composeapp.generated.resources.Settings_SendEmail_Label +import ooniprobe.composeapp.generated.resources.Settings_Sharing_UploadResults +import ooniprobe.composeapp.generated.resources.Settings_Storage_Label import ooniprobe.composeapp.generated.resources.Settings_TestOptions_Label -import ooniprobe.composeapp.generated.resources.about_ooni +import ooniprobe.composeapp.generated.resources.Settings_WarmVPNInUse_Label +import ooniprobe.composeapp.generated.resources.Settings_Websites_Categories_Description +import ooniprobe.composeapp.generated.resources.Settings_Websites_Categories_Label +import ooniprobe.composeapp.generated.resources.Settings_Websites_MaxRuntime +import ooniprobe.composeapp.generated.resources.Settings_Websites_MaxRuntimeEnabled import ooniprobe.composeapp.generated.resources.advanced +import ooniprobe.composeapp.generated.resources.category_aldr +import ooniprobe.composeapp.generated.resources.category_anon +import ooniprobe.composeapp.generated.resources.category_comm +import ooniprobe.composeapp.generated.resources.category_comt +import ooniprobe.composeapp.generated.resources.category_ctrl +import ooniprobe.composeapp.generated.resources.category_cultr +import ooniprobe.composeapp.generated.resources.category_date +import ooniprobe.composeapp.generated.resources.category_econ +import ooniprobe.composeapp.generated.resources.category_env +import ooniprobe.composeapp.generated.resources.category_file +import ooniprobe.composeapp.generated.resources.category_game +import ooniprobe.composeapp.generated.resources.category_gmb +import ooniprobe.composeapp.generated.resources.category_govt +import ooniprobe.composeapp.generated.resources.category_grp +import ooniprobe.composeapp.generated.resources.category_hack +import ooniprobe.composeapp.generated.resources.category_hate +import ooniprobe.composeapp.generated.resources.category_host +import ooniprobe.composeapp.generated.resources.category_humr +import ooniprobe.composeapp.generated.resources.category_igo +import ooniprobe.composeapp.generated.resources.category_lgbt +import ooniprobe.composeapp.generated.resources.category_milx +import ooniprobe.composeapp.generated.resources.category_mmed +import ooniprobe.composeapp.generated.resources.category_news +import ooniprobe.composeapp.generated.resources.category_polr +import ooniprobe.composeapp.generated.resources.category_porn +import ooniprobe.composeapp.generated.resources.category_prov +import ooniprobe.composeapp.generated.resources.category_pubh +import ooniprobe.composeapp.generated.resources.category_rel +import ooniprobe.composeapp.generated.resources.category_srch +import ooniprobe.composeapp.generated.resources.category_xed import ooniprobe.composeapp.generated.resources.ic_settings import ooniprobe.composeapp.generated.resources.notifications -import ooniprobe.composeapp.generated.resources.ooni_backend_proxy import ooniprobe.composeapp.generated.resources.outline_info import ooniprobe.composeapp.generated.resources.privacy import ooniprobe.composeapp.generated.resources.proxy import ooniprobe.composeapp.generated.resources.send_email import ooniprobe.composeapp.generated.resources.settings -import ooniprobe.composeapp.generated.resources.test_options import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource +import org.ooni.probe.data.repositories.PreferenceCategoryKey +import org.ooni.probe.data.repositories.SettingsKey +import org.ooni.probe.ui.settings.category.SettingsDescription @Composable fun SettingsScreen(onNavigateToSettingsCategory: (SettingsViewModel.Event) -> Unit) { @@ -43,110 +150,454 @@ fun SettingsScreen(onNavigateToSettingsCategory: (SettingsViewModel.Event) -> Un Text(stringResource(Res.string.settings)) }, ) - SettingsItem( - icon = Res.drawable.notifications, - title = Res.string.Settings_Notifications_Label, - modifier = - stringResource(Res.string.notifications).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.ic_settings, - title = Res.string.Settings_TestOptions_Label, - modifier = - stringResource(Res.string.test_options).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.privacy, - title = Res.string.Settings_Privacy_Label, - modifier = - stringResource(Res.string.privacy).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.proxy, - title = Res.string.Settings_Proxy_Label, - modifier = - stringResource(Res.string.ooni_backend_proxy).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.advanced, - title = Res.string.Settings_Advanced_Label, - modifier = - stringResource(Res.string.advanced).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.send_email, - title = Res.string.Settings_SendEmail_Label, - modifier = - stringResource(Res.string.send_email).let { category -> - Modifier.clickable { - onNavigateToSettingsCategory( - category.routeToSettingsCategory(), - ) - } - }, - ) - SettingsItem( - icon = Res.drawable.outline_info, - title = Res.string.Settings_About_Label, - modifier = - stringResource(Res.string.about_ooni).let { category -> + + SettingsCategoryItem.getSettingsItems().forEach { item -> + SettingsItemView( + icon = item.icon, + title = item.title, + modifier = Modifier.clickable { onNavigateToSettingsCategory( - category.routeToSettingsCategory(), + item.routeToSettingsCategory(), ) - } - }, - ) + }, + ) + } } } -fun String.routeToSettingsCategory() = SettingsViewModel.Event.SettingsCategoryClick(this) - @Composable -fun SettingsItem( - icon: DrawableResource, +fun SettingsItemView( + icon: DrawableResource?, title: StringResource, modifier: Modifier, ) { ListItem( leadingContent = { - Image( - modifier = Modifier.height(24.dp).width(24.dp), - painter = painterResource(icon), - contentDescription = stringResource(title), - ) + icon?.let { + Image( + modifier = Modifier.height(24.dp).width(24.dp), + painter = painterResource(it), + contentDescription = stringResource(title), + ) + } }, headlineContent = { Text(stringResource(title)) }, modifier = modifier, ) } + +open class PreferenceItem( + open val title: StringResource, + open val icon: DrawableResource? = null, + open val type: PreferenceItemType, + open val key: String, + open val supportingContent: + @Composable() + (() -> Unit)? = null, +) + +data class SettingsItem( + override val icon: DrawableResource? = null, + override val title: StringResource, + override val type: PreferenceItemType, + override val key: String, + val children: List? = emptyList(), + override val supportingContent: + @Composable() + (() -> Unit)? = null, +) : PreferenceItem(title = title, icon = icon, supportingContent = supportingContent, type = type, key = key) + +data class SettingsCategoryItem( + override val icon: DrawableResource? = null, + override val title: StringResource, + val route: PreferenceCategoryKey, + val settings: List? = emptyList(), + override val supportingContent: + @Composable (() -> Unit)? = null, + val footerContent: + @Composable (() -> Unit)? = null, +) : PreferenceItem( + title = title, + icon = icon, + supportingContent = supportingContent, + type = PreferenceItemType.ROUTE, + key = route.value, + ) { + fun routeToSettingsCategory() = SettingsViewModel.Event.SettingsCategoryClick(route) + + companion object { + private val seeRecentLogsCategory = + SettingsCategoryItem( + title = Res.string.Settings_Advanced_RecentLogs, + route = PreferenceCategoryKey.SEE_RECENT_LOGS, + ) + private val webCategory = + SettingsCategoryItem( + title = Res.string.Settings_Websites_Categories_Label, + route = PreferenceCategoryKey.WEBSITES_CATEGORIES, + supportingContent = { + Text(stringResource(Res.string.Settings_Websites_Categories_Description)) + }, + settings = + listOf( + SettingsItem( + icon = Res.drawable.category_anon, + title = Res.string.CategoryCode_ANON_Name, + supportingContent = { + Text(stringResource(Res.string.CategoryCode_ANON_Description)) + }, + key = SettingsKey.ANON.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_comt, + title = Res.string.CategoryCode_COMT_Name, + supportingContent = { + Text(stringResource(Res.string.CategoryCode_COMT_Description)) + }, + key = SettingsKey.COMT.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_ctrl, + title = Res.string.CategoryCode_CTRL_Name, + supportingContent = { + Text(stringResource(Res.string.CategoryCode_CTRL_Description)) + }, + key = SettingsKey.CTRL.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_cultr, + title = Res.string.CategoryCode_CULTR_Name, + supportingContent = { + Text(stringResource(Res.string.CategoryCode_CULTR_Description)) + }, + key = SettingsKey.CULTR.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_aldr, + title = Res.string.CategoryCode_ALDR_Name, + supportingContent = { + Text(stringResource(Res.string.CategoryCode_ALDR_Description)) + }, + key = SettingsKey.ALDR.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_comm, + title = Res.string.CategoryCode_COMM_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_COMM_Description)) }, + key = SettingsKey.COMM.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_econ, + title = Res.string.CategoryCode_ECON_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_ECON_Description)) }, + key = SettingsKey.ECON.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_env, + title = Res.string.CategoryCode_ENV_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_ENV_Description)) }, + key = SettingsKey.ENV.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_file, + title = Res.string.CategoryCode_FILE_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_FILE_Description)) }, + key = SettingsKey.FILE.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_gmb, + title = Res.string.CategoryCode_GMB_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_GMB_Description)) }, + key = SettingsKey.GMB.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_game, + title = Res.string.CategoryCode_GAME_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_GAME_Description)) }, + key = SettingsKey.GAME.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_govt, + title = Res.string.CategoryCode_GOVT_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_GOVT_Description)) }, + key = SettingsKey.GOVT.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_hack, + title = Res.string.CategoryCode_HACK_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_HACK_Description)) }, + key = SettingsKey.HACK.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_hate, + title = Res.string.CategoryCode_HATE_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_HATE_Description)) }, + key = SettingsKey.HATE.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_host, + title = Res.string.CategoryCode_HOST_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_HOST_Description)) }, + key = SettingsKey.HOST.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_humr, + title = Res.string.CategoryCode_HUMR_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_HUMR_Description)) }, + key = SettingsKey.HUMR.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_igo, + title = Res.string.CategoryCode_IGO_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_IGO_Description)) }, + key = SettingsKey.IGO.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_lgbt, + title = Res.string.CategoryCode_LGBT_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_LGBT_Description)) }, + key = SettingsKey.LGBT.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_mmed, + title = Res.string.CategoryCode_MMED_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_MMED_Description)) }, + key = SettingsKey.MMED.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_news, + title = Res.string.CategoryCode_NEWS_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_NEWS_Description)) }, + key = SettingsKey.NEWS.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_date, + title = Res.string.CategoryCode_DATE_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_DATE_Description)) }, + key = SettingsKey.DATE.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_polr, + title = Res.string.CategoryCode_POLR_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_POLR_Description)) }, + key = SettingsKey.POLR.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_porn, + title = Res.string.CategoryCode_PORN_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_PORN_Description)) }, + key = SettingsKey.PORN.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_prov, + title = Res.string.CategoryCode_PROV_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_PROV_Description)) }, + key = SettingsKey.PROV.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_pubh, + title = Res.string.CategoryCode_PUBH_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_PUBH_Description)) }, + key = SettingsKey.PUBH.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_rel, + title = Res.string.CategoryCode_REL_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_REL_Description)) }, + key = SettingsKey.REL.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_srch, + title = Res.string.CategoryCode_SRCH_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_SRCH_Description)) }, + key = SettingsKey.SRCH.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_xed, + title = Res.string.CategoryCode_XED_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_XED_Description)) }, + key = SettingsKey.XED.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_grp, + title = Res.string.CategoryCode_GRP_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_GRP_Description)) }, + key = SettingsKey.GRP.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + icon = Res.drawable.category_milx, + title = Res.string.CategoryCode_MILX_Name, + supportingContent = { Text(stringResource(Res.string.CategoryCode_MILX_Description)) }, + key = SettingsKey.MILX.value, + type = PreferenceItemType.SWITCH, + ), + ), + ) + + fun getSettingsItems() = + listOf( + SettingsCategoryItem( + icon = Res.drawable.notifications, + title = Res.string.Settings_Notifications_Label, + route = PreferenceCategoryKey.NOTIFICATIONS, + settings = + listOf( + SettingsItem( + title = Res.string.Settings_Notifications_Enabled, + key = SettingsKey.NOTIFICATIONS_ENABLED.value, + type = PreferenceItemType.SWITCH, + ), + ), + footerContent = { + SettingsDescription( + Res.string.Modal_EnableNotifications_Paragraph, + ) + }, + ), + SettingsCategoryItem( + icon = Res.drawable.ic_settings, + title = Res.string.Settings_TestOptions_Label, + route = PreferenceCategoryKey.TEST_OPTIONS, + settings = + listOf( + SettingsItem( + title = Res.string.Settings_AutomatedTesting_RunAutomatically, + key = SettingsKey.AUTOMATED_TESTING_ENABLED.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + title = Res.string.Settings_AutomatedTesting_RunAutomatically_WiFiOnly, + key = SettingsKey.AUTOMATED_TESTING_WIFIONLY.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + title = Res.string.Settings_AutomatedTesting_RunAutomatically_ChargingOnly, + key = SettingsKey.AUTOMATED_TESTING_CHARGING.value, + type = PreferenceItemType.SWITCH, + ), + webCategory, + SettingsItem( + title = Res.string.Settings_Websites_MaxRuntimeEnabled, + key = SettingsKey.MAX_RUNTIME_ENABLED.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + title = Res.string.Settings_Websites_MaxRuntime, + key = SettingsKey.MAX_RUNTIME.value, + type = PreferenceItemType.TEXT, + ), + ), + footerContent = { + SettingsDescription( + Res.string.Settings_AutomatedTesting_RunAutomatically_Footer, + ) + }, + ), + SettingsCategoryItem( + icon = Res.drawable.privacy, + title = Res.string.Settings_Privacy_Label, + route = PreferenceCategoryKey.PRIVACY, + settings = + listOf( + SettingsItem( + title = Res.string.Settings_Sharing_UploadResults, + key = SettingsKey.UPLOAD_RESULTS.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + title = Res.string.Settings_Privacy_SendCrashReports, + key = SettingsKey.SEND_CRASH.value, + type = PreferenceItemType.SWITCH, + ), + ), + ), + SettingsCategoryItem( + icon = Res.drawable.proxy, + title = Res.string.Settings_Proxy_Label, + route = PreferenceCategoryKey.PROXY, + ), + SettingsCategoryItem( + icon = Res.drawable.advanced, + title = Res.string.Settings_Advanced_Label, + route = PreferenceCategoryKey.ADVANCED, + settings = + listOf( + SettingsItem( + title = Res.string.Settings_Advanced_LanguageSettings_Title, + key = SettingsKey.LANGUAGE_SETTING.value, + type = PreferenceItemType.SELECT, + ), + seeRecentLogsCategory, + SettingsItem( + title = Res.string.Settings_Advanced_DebugLogs, + key = SettingsKey.DEBUG_LOGS.value, + type = PreferenceItemType.SWITCH, + ), + SettingsItem( + title = Res.string.Settings_Storage_Label, + key = SettingsKey.STORAGE_SIZE.value, + type = PreferenceItemType.BUTTON, + ), + SettingsItem( + title = Res.string.Settings_WarmVPNInUse_Label, + key = SettingsKey.WARN_VPN_IN_USE.value, + type = PreferenceItemType.SWITCH, + ), + ), + ), + SettingsCategoryItem( + icon = Res.drawable.send_email, + title = Res.string.Settings_SendEmail_Label, + route = PreferenceCategoryKey.SEND_EMAIL, + ), + SettingsCategoryItem( + icon = Res.drawable.outline_info, + title = Res.string.Settings_About_Label, + route = PreferenceCategoryKey.ABOUT_OONI, + ), + ) + + fun getSettingsItem(route: PreferenceCategoryKey) = + (getSettingsItems() + listOf(webCategory, seeRecentLogsCategory)).first { + it.route == route + } + } +} + +enum class PreferenceItemType { + SWITCH, + TEXT, + BUTTON, + SELECT, + ROUTE, +} diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsViewModel.kt index 106b4009..7549e115 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/SettingsViewModel.kt @@ -6,9 +6,10 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.ooni.probe.data.repositories.PreferenceCategoryKey open class SettingsViewModel( - goToSettingsForCategory: (String) -> Unit, + goToSettingsForCategory: (PreferenceCategoryKey) -> Unit, ) : ViewModel() { private val events = MutableSharedFlow(extraBufferCapacity = 1) @@ -22,6 +23,6 @@ open class SettingsViewModel( } sealed interface Event { - data class SettingsCategoryClick(val category: String) : Event + data class SettingsCategoryClick(val category: PreferenceCategoryKey) : Event } } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryScreen.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryScreen.kt index 2ea875ca..d2d1e5e5 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryScreen.kt @@ -1,9 +1,17 @@ package org.ooni.probe.ui.settings.category +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -12,88 +20,27 @@ import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import ooniprobe.composeapp.generated.resources.Modal_EnableNotifications_Paragraph import ooniprobe.composeapp.generated.resources.Res -import ooniprobe.composeapp.generated.resources.Settings_About_Label -import ooniprobe.composeapp.generated.resources.Settings_Advanced_Label -import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically -import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_ChargingOnly -import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_Footer -import ooniprobe.composeapp.generated.resources.Settings_AutomatedTesting_RunAutomatically_WiFiOnly -import ooniprobe.composeapp.generated.resources.Settings_Notifications_Enabled -import ooniprobe.composeapp.generated.resources.Settings_Notifications_Label -import ooniprobe.composeapp.generated.resources.Settings_Privacy_Label -import ooniprobe.composeapp.generated.resources.Settings_Privacy_SendCrashReports -import ooniprobe.composeapp.generated.resources.Settings_Proxy_Label -import ooniprobe.composeapp.generated.resources.Settings_Sharing_UploadResults -import ooniprobe.composeapp.generated.resources.Settings_TestOptions_Label -import ooniprobe.composeapp.generated.resources.Settings_Websites_Categories_Description -import ooniprobe.composeapp.generated.resources.Settings_Websites_Categories_Label -import ooniprobe.composeapp.generated.resources.Settings_Websites_MaxRuntime -import ooniprobe.composeapp.generated.resources.Settings_Websites_MaxRuntimeEnabled -import ooniprobe.composeapp.generated.resources.about_ooni -import ooniprobe.composeapp.generated.resources.advanced -import ooniprobe.composeapp.generated.resources.automated_testing_charging -import ooniprobe.composeapp.generated.resources.automated_testing_enabled -import ooniprobe.composeapp.generated.resources.automated_testing_wifionly import ooniprobe.composeapp.generated.resources.back -import ooniprobe.composeapp.generated.resources.max_runtime -import ooniprobe.composeapp.generated.resources.max_runtime_enabled -import ooniprobe.composeapp.generated.resources.notifications -import ooniprobe.composeapp.generated.resources.notifications_enabled -import ooniprobe.composeapp.generated.resources.ooni_backend_proxy -import ooniprobe.composeapp.generated.resources.privacy -import ooniprobe.composeapp.generated.resources.send_crash -import ooniprobe.composeapp.generated.resources.test_options -import ooniprobe.composeapp.generated.resources.upload_results +import ooniprobe.composeapp.generated.resources.settings import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource +import org.ooni.probe.data.repositories.PreferenceCategoryKey +import org.ooni.probe.ui.settings.PreferenceItemType @Composable fun SettingsCategoryScreen( - category: String, + state: SettingsCategoryViewModel.State, onEvent: (SettingsCategoryViewModel.Event) -> Unit, ) { - val categories = - mapOf( - stringResource(Res.string.notifications) to - SettingsCategory( - title = Res.string.Settings_Notifications_Label, - content = { NotificationsSettingsScreen(onEvent) }, - ), - stringResource(Res.string.test_options) to - SettingsCategory( - title = Res.string.Settings_TestOptions_Label, - content = { TestOptionsSettingsScreen(onEvent) }, - ), - stringResource(Res.string.privacy) to - SettingsCategory( - title = Res.string.Settings_Privacy_Label, - content = { PrivacySettingsScreen(onEvent) }, - ), - stringResource(Res.string.advanced) to - SettingsCategory( - title = Res.string.Settings_Advanced_Label, - content = { return@SettingsCategory }, - ), - stringResource(Res.string.ooni_backend_proxy) to - SettingsCategory( - title = Res.string.Settings_Proxy_Label, - content = { return@SettingsCategory }, - ), - stringResource(Res.string.about_ooni) to - SettingsCategory( - title = Res.string.Settings_About_Label, - content = { return@SettingsCategory }, - ), - ) - Column { TopAppBar( title = { - categories[category]?.let { Text(stringResource(it.title)) } + Text(stringResource(state.category.title)) }, navigationIcon = { IconButton(onClick = { onEvent(SettingsCategoryViewModel.Event.BackClicked) }) { @@ -104,166 +51,91 @@ fun SettingsCategoryScreen( } }, ) - categories[category]?.content?.invoke() - } -} + Box( + modifier = Modifier.verticalScroll(rememberScrollState()).padding(bottom = 48.dp), + ) { + Column { + state.category.settings?.forEach { preferenceItem -> + when (preferenceItem.type) { + PreferenceItemType.SWITCH -> + SwitchSettingsView( + leadingContent = + preferenceItem.icon?.let { + { + Image( + modifier = Modifier.height(24.dp).width(24.dp), + painter = painterResource(it), + contentDescription = stringResource(preferenceItem.title), + ) + } + }, + title = preferenceItem.title, + key = preferenceItem.key, + checked = + state.preference?.let { it[preferenceItem.key] as? Boolean } + ?: false, + supportingContent = preferenceItem.supportingContent, + onCheckedChange = { key, value -> + onEvent( + SettingsCategoryViewModel.Event.CheckedChangeClick( + key, + value, + ), + ) + }, + ) -data class SettingsCategory( - val title: StringResource, - val content: @Composable () -> Unit, -) + PreferenceItemType.TEXT -> + RouteSettingsView( + title = preferenceItem.title, + supportingContent = preferenceItem.supportingContent, + ) -@Composable -fun NotificationsSettingsScreen(onEvent: (SettingsCategoryViewModel.Event) -> Unit) { - Column { - SwitchSettings( - title = Res.string.Settings_Notifications_Enabled, - key = stringResource(Res.string.notifications_enabled), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - SettingsDescription( - Res.string.Modal_EnableNotifications_Paragraph, - ) - } -} + PreferenceItemType.BUTTON -> + RouteSettingsView( + title = preferenceItem.title, + supportingContent = preferenceItem.supportingContent, + trailingContent = { + Button( + onClick = {}, + ) { + Text(stringResource(Res.string.settings)) + } + }, + ) -@Composable -fun TestOptionsSettingsScreen(onEvent: (SettingsCategoryViewModel.Event) -> Unit) { - Column { - SwitchSettings( - title = Res.string.Settings_AutomatedTesting_RunAutomatically, - key = stringResource(Res.string.automated_testing_enabled), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - // TODO: Add dependency on status of automated testing above - SwitchSettings( - title = Res.string.Settings_AutomatedTesting_RunAutomatically_WiFiOnly, - key = stringResource(Res.string.automated_testing_wifionly), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - SwitchSettings( - title = Res.string.Settings_AutomatedTesting_RunAutomatically_ChargingOnly, - key = stringResource(Res.string.automated_testing_charging), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - // TODO: add proper structure to navigate to the websites categories - SwitchSettings( - title = Res.string.Settings_Websites_Categories_Label, - supportingContent = { - Text(stringResource(Res.string.Settings_Websites_Categories_Description)) - }, - key = stringResource(Res.string.automated_testing_charging), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - SwitchSettings( - title = Res.string.Settings_Websites_MaxRuntimeEnabled, - key = stringResource(Res.string.max_runtime_enabled), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - // Add proper view to enter and validate value. - SwitchSettings( - title = Res.string.Settings_Websites_MaxRuntime, - key = stringResource(Res.string.max_runtime), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - SettingsDescription( - Res.string.Settings_AutomatedTesting_RunAutomatically_Footer, - ) - } -} + PreferenceItemType.ROUTE -> + RouteSettingsView( + title = preferenceItem.title, + supportingContent = preferenceItem.supportingContent, + modifier = + Modifier.clickable { + onEvent( + SettingsCategoryViewModel.Event.SettingsCategoryClick( + PreferenceCategoryKey.valueOf(preferenceItem.key.uppercase()), + ), + ) + }, + ) -@Composable -fun PrivacySettingsScreen(onEvent: (SettingsCategoryViewModel.Event) -> Unit) { - Column { - SwitchSettings( - title = Res.string.Settings_Sharing_UploadResults, - key = stringResource(Res.string.upload_results), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) - SwitchSettings( - title = Res.string.Settings_Privacy_SendCrashReports, - key = stringResource(Res.string.send_crash), - checked = false, - onCheckedChange = { key, value -> - onEvent( - SettingsCategoryViewModel.Event.CheckedChangeClick( - key, - value, - ), - ) - }, - ) + PreferenceItemType.SELECT -> + RouteSettingsView( + title = preferenceItem.title, + supportingContent = preferenceItem.supportingContent, + ) + } + } + state.category.footerContent?.invoke() + } + } } } @Composable -fun SwitchSettings( +fun SwitchSettingsView( title: StringResource, supportingContent: @Composable (() -> Unit)? = null, + leadingContent: @Composable (() -> Unit)? = null, key: String, checked: Boolean, onCheckedChange: (String, Boolean) -> Unit, @@ -271,16 +143,39 @@ fun SwitchSettings( ListItem( headlineContent = { Text(stringResource(title)) }, supportingContent = supportingContent, + leadingContent = leadingContent, trailingContent = { Switch( checked = checked, onCheckedChange = { newValue -> onCheckedChange(key, newValue) }, + modifier = Modifier.scale(0.7f), ) }, ) } +@Composable +fun RouteSettingsView( + title: StringResource, + supportingContent: @Composable (() -> Unit)? = null, + leadingContent: @Composable (() -> Unit)? = null, + trailingContent: @Composable (() -> Unit)? = null, + modifier: Modifier = Modifier, +) { + ListItem( + headlineContent = { Text(stringResource(title)) }, + supportingContent = supportingContent, + leadingContent = leadingContent, + trailingContent = trailingContent, + modifier = modifier, + ) +} + @Composable fun SettingsDescription(description: StringResource) { - Text(stringResource(description), modifier = Modifier.padding(horizontal = 16.dp), fontSize = 12.sp) + Text( + stringResource(description), + modifier = Modifier.padding(horizontal = 16.dp), + fontSize = 12.sp, + ) } diff --git a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryViewModel.kt b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryViewModel.kt index 34951614..2ac69b2b 100644 --- a/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/ooni/probe/ui/settings/category/SettingsCategoryViewModel.kt @@ -1,48 +1,66 @@ package org.ooni.probe.ui.settings.category import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.intPreferencesKey import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import org.ooni.probe.data.repositories.PreferenceCategoryKey import org.ooni.probe.data.repositories.PreferenceRepository -import org.ooni.probe.data.repositories.SettingsKey +import org.ooni.probe.ui.settings.SettingsCategoryItem class SettingsCategoryViewModel( preferenceManager: PreferenceRepository, - goToSettingsForCategory: (String) -> Unit, + goToSettingsForCategory: (PreferenceCategoryKey) -> Unit, onBack: () -> Unit, + category: SettingsCategoryItem, ) : ViewModel() { private val events = MutableSharedFlow(extraBufferCapacity = 1) + private val _state = MutableStateFlow(State(preference = null, category = category)) + val state = _state.asStateFlow() + init { + category.settings?.map { item -> booleanPreferencesKey(item.key) }?.let { preferenceKeys -> + preferenceManager.allSettings(preferenceKeys) + .onEach { result -> _state.update { it.copy(preference = result) } } + .launchIn(viewModelScope) + } + events.filterIsInstance() .onEach { goToSettingsForCategory(it.category) }.launchIn(viewModelScope) - events.filterIsInstance() - .onEach { onBack() }.launchIn(viewModelScope) + + events.filterIsInstance().onEach { + preferenceManager.setValueByKey( + booleanPreferencesKey(it.key), + it.value, + ) + _state.update { state -> + state.copy( + preference = state.preference?.plus(it.key to it.value), + ) + } + }.launchIn(viewModelScope) + + events.filterIsInstance().onEach { onBack() }.launchIn(viewModelScope) } fun onEvent(event: Event) { events.tryEmit(event) } - val settings: StateFlow?> = - preferenceManager - .allSettings(listOf(booleanPreferencesKey(SettingsKey.NOTIFICATIONS_ENABLED.value))) - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5000L), - null, - ) + data class State( + val preference: Map?, + val category: SettingsCategoryItem, + ) sealed interface Event { - data class SettingsCategoryClick(val category: String) : Event + data class SettingsCategoryClick(val category: PreferenceCategoryKey) : Event data class CheckedChangeClick(val key: String, val value: Boolean) : Event diff --git a/composeApp/src/commonTest/kotlin/org/ooni/probe/data/repositories/PreferenceRepositoryTest.kt b/composeApp/src/commonTest/kotlin/org/ooni/probe/data/repositories/PreferenceRepositoryTest.kt index 4d7321d0..786658a1 100644 --- a/composeApp/src/commonTest/kotlin/org/ooni/probe/data/repositories/PreferenceRepositoryTest.kt +++ b/composeApp/src/commonTest/kotlin/org/ooni/probe/data/repositories/PreferenceRepositoryTest.kt @@ -20,28 +20,30 @@ class PreferenceRepositoryTest { } @AfterTest - fun after() = runBlocking { - runTest { - preferenceRepository.clear() + fun after() = + runBlocking { + runTest { + preferenceRepository.clear() + } } - } @Test - fun testAllSettings() = runBlocking { - runTest { - val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) - val value = "value" - preferenceRepository.setValueByKey(key, value) - val setting: Map = preferenceRepository.allSettings(listOf(key)).first() - assertEquals(value, setting.values.first()) + fun testAllSettings() = + runBlocking { + runTest { + val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) + val value = "value" + preferenceRepository.setValueByKey(key, value) + val setting: Map = preferenceRepository.allSettings(listOf(key)).first() + assertEquals(value, setting.values.first()) + } } - } @Test fun testGetPreferenceKey() { assertEquals( SettingsKey.LANGUAGE_SETTING.value, - preferenceRepository.getPreferenceKey(SettingsKey.LANGUAGE_SETTING.value) + preferenceRepository.getPreferenceKey(SettingsKey.LANGUAGE_SETTING.value), ) assertEquals( "prefix_${SettingsKey.LANGUAGE_SETTING.value}", @@ -50,59 +52,66 @@ class PreferenceRepositoryTest { assertEquals( "${SettingsKey.LANGUAGE_SETTING.value}_autorun", preferenceRepository.getPreferenceKey( - SettingsKey.LANGUAGE_SETTING.value, autoRun = true + SettingsKey.LANGUAGE_SETTING.value, + autoRun = true, ), ) assertEquals( "prefix_${SettingsKey.LANGUAGE_SETTING.value}_autorun", preferenceRepository.getPreferenceKey( - SettingsKey.LANGUAGE_SETTING.value, "prefix", true + SettingsKey.LANGUAGE_SETTING.value, + "prefix", + true, ), ) } @Test - fun testGetValueByKey() = runTest { - val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) - val value = "value" - preferenceRepository.setValueByKey(key, value) - assertEquals(value, preferenceRepository.getValueByKey(key = key).first()) - } - - - @Test - fun testSetValueByKey() = runTest { - val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) - val value = "value" - preferenceRepository.setValueByKey(key, value) - assertEquals(value, preferenceRepository.getValueByKey(key).first()) - } + fun testGetValueByKey() = + runTest { + val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) + val value = "value" + preferenceRepository.setValueByKey(key, value) + assertEquals(value, preferenceRepository.getValueByKey(key = key).first()) + } @Test - fun testClear() = runTest { - val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) - val value = "value" - preferenceRepository.setValueByKey(key, value) - preferenceRepository.clear() - assertNull(preferenceRepository.getValueByKey(key).first()) - } + fun testSetValueByKey() = + runTest { + val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) + val value = "value" + preferenceRepository.setValueByKey(key, value) + assertEquals(value, preferenceRepository.getValueByKey(key).first()) + } @Test - fun testRemove() = runTest { - val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) - val value = "value" - preferenceRepository.setValueByKey(key, value) - preferenceRepository.remove(key) - assertNull(preferenceRepository.getValueByKey(key).first()) - } + fun testClear() = + runTest { + val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) + val value = "value" + preferenceRepository.setValueByKey(key, value) + preferenceRepository.clear() + assertNull(preferenceRepository.getValueByKey(key).first()) + } @Test - fun testContains() = runBlocking { + fun testRemove() = runTest { val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) val value = "value" preferenceRepository.setValueByKey(key, value) - assertEquals(true, preferenceRepository.contains(key)) + preferenceRepository.remove(key) + assertNull(preferenceRepository.getValueByKey(key).first()) + } + + @Test + fun testContains() = + runBlocking { + runTest { + val key = stringPreferencesKey(SettingsKey.LANGUAGE_SETTING.value) + val value = "value" + preferenceRepository.setValueByKey(key, value) + assertEquals(true, preferenceRepository.contains(key)) + } } - } }