From 387db176f1b06ae3b63b9df82f084c4ad8659b1f Mon Sep 17 00:00:00 2001 From: Guillermo Villafuerte Date: Wed, 27 Dec 2023 22:48:01 -0600 Subject: [PATCH 01/39] Add menu entry "Edit widgets" if button on the widgets list is not shown. Signed-off-by: Guillermo Villafuerte --- .../ui/launcher/searchbar/SearchBarMenu.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt index 632b1584..746b58fb 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt @@ -9,6 +9,7 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.layout.RowScope import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.HelpOutline import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Wallpaper @@ -20,6 +21,7 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -27,7 +29,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM +import de.mm20.launcher2.ui.launcher.widgets.WidgetsVM import de.mm20.launcher2.ui.settings.SettingsActivity @Composable @@ -38,6 +43,8 @@ fun RowScope.SearchBarMenu( val context = LocalContext.current var showOverflowMenu by remember { mutableStateOf(false) } val rightIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_menu_clear) + val launcherVM: LauncherScaffoldVM = viewModel() + val widgetsVM: WidgetsVM = viewModel() IconButton(onClick = { if (searchBarValue.isNotBlank()) onSearchBarValueChange("") @@ -70,6 +77,21 @@ fun RowScope.SearchBarMenu( Icon(imageVector = Icons.Rounded.Wallpaper, contentDescription = null) } ) + val editButton by widgetsVM.editButton.collectAsState() + if (editButton == false) { + DropdownMenuItem( + onClick = { + launcherVM.setWidgetEditMode(editMode = true) + showOverflowMenu = false + }, + text = { + Text(stringResource(R.string.menu_edit_widgets)) + }, + leadingIcon = { + Icon(imageVector = Icons.Rounded.Edit, contentDescription = null) + } + ) + } DropdownMenuItem( onClick = { context.startActivity(Intent(context, SettingsActivity::class.java)) From 79e09c197b10f739ad1a8c7cc3a1675da69474bc Mon Sep 17 00:00:00 2001 From: Guillermo Villafuerte Date: Thu, 28 Dec 2023 11:54:37 -0600 Subject: [PATCH 02/39] FIX: "Add widget" button not visible if edit mode was activated from search bar menu. Signed-off-by: Guillermo Villafuerte --- .../java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt index 65cc2849..1edf46c1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt @@ -149,7 +149,7 @@ fun WidgetColumn( } val editButton by viewModel.editButton.collectAsState() - if (editButton == true) { + if (editMode || editButton == true) { val icon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_edit_add) ExtendedFloatingActionButton( From ef0e2ed57a5691abe53e3417f210bfb09b298131 Mon Sep 17 00:00:00 2001 From: Guillermo Villafuerte Date: Thu, 28 Dec 2023 01:22:47 +0000 Subject: [PATCH 03/39] Translated using Weblate (Spanish) Currently translated at 99.8% (647 of 648 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-es/strings.xml | 50 +++++++++++++++++--- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/core/i18n/src/main/res/values-es/strings.xml b/core/i18n/src/main/res/values-es/strings.xml index e224fa2d..e9bad0d3 100644 --- a/core/i18n/src/main/res/values-es/strings.xml +++ b/core/i18n/src/main/res/values-es/strings.xml @@ -42,7 +42,7 @@ Artista: %1$s Álbum: %1$s Duración: %1$s - %1$s link + Enlace de %1$s Año: %1$s Tamaño: %1$s Ruta: %1$s @@ -151,7 +151,7 @@ Abrir sitio web Información de la aplicación y la licencia Proveedor - AQUÍ + HERE Ubicación Ubicación automática Ubicación @@ -369,7 +369,7 @@ Creado %1$s en %2$s con %3$s. Seleccione que restaurar. ¡Los datos existentes van a ser sobrescritos! Por defecto - Buscar icono + Buscar en paquetes de iconos Personal Trabajo Los elementos fijados y frecuentemente usados aparecerán aquí @@ -406,7 +406,7 @@ %1$d elementos seleccionados Mostrar insignia para archivos almacenados en la nube - Mostrar el teclado automáticamente al abrir la búsqueda + Mostrar el teclado automáticamente al abrir la lista de aplicaciones Administrar aplicaciones y resultados de búsqueda ocultos NextCloud Registrate para buscar en tu servidor NextCloud @@ -416,7 +416,7 @@ Herramientas de solución de problemas Selecciona componentes para hacer copia de seguridad: Diseño - Las cuentas conectadas y aplicaciones de terceros no serán incluidas en la copia de seguridad. + Las cuentas conectadas no serán incluidas en la copia de seguridad. Los widgets de apps de terceros solo pueden ser restaurados en este dispositivo. Por defecto Esta copia de seguridad se ha creado con una diferente versión de %1$s y no se puede restaurar con esta versión. Rellenar altura de pantalla @@ -432,7 +432,7 @@ Deslizar hacia la derecha Doble toque Pulsación larga - Nada + No hacer nada Abrir búsqueda Abrir barra de notificaciones Apagar pantalla @@ -608,7 +608,7 @@ Mostrar un botón para revelar los resultados de búsqueda ocultos Calendarios no encontrados Orden de resultados de búsqueda - El widget de la aplicación a fallado en cargar. + El widget de la aplicación ha fallado en cargar. El archivo vinculado no está vacío y su contenido no coincide con la última versión guardada de esta nota. ¿Qué versión desea conservar\? No se ha podido leer el archivo vinculado. Posiblemente, haya sido movido o borrado. Se ha restaurado una copia desde el almacenamiento interno del lanzador. Si editas la nota, posiblemente se sobrescribirá el archivo vinculado. Cuentas @@ -634,4 +634,40 @@ Error al leer la nota Error al guardar la nota No se ha podido leer el archivo vinculado. Posiblemente, haya sido movido o borrado. Se ha restaurado una copia desde el almacenamiento interno del lanzador. Si editas la nota, posiblemente se sobrescribirá el archivo vinculado. + Tema de %1$s + Captura una instantánea para analizar el uso de memoria. La app se congelará hasta que el proceso se complete. + Usar el predeterminado del sistema + Alineación + Restaurar predeterminado + Búsqueda web + ¿Realmente deseas eliminar el esquema de colores %1$s? + El archivo seleccionado no pudo ser leído. Asegúrate de que has seleccionado un archivo de tema válido (*.kvtheme), y que el archivo no esté dañado. + Inferior + Insignias de plugin + Indica con qué plugin se creó un resultado de búsqueda + Centro + Esquemas de color + Editar + El permiso de plugin es requerido para usar plugins. + Plugins + Administrar extensiones instaladas + Sin plugins instalados + Host de plugins no instalado + Animaciones + Animación de carga + Reproduce una animación de burbujas mientras el dispositivo está cargando + Instantánea de memoria + Capturando instantánea… + Zona dinámica + Radio de desenfoque + Paleta de colores + Personalizado + Importar + Superior + Nuevo esquema de colores + A partir de color primario + Paleta + Aplicar tema + No disponible + Este acceso directo no está disponible porque %1$s no es el lanzador predeterminado \ No newline at end of file From 5a44a0190a7abab00303f754dff257e989c148db Mon Sep 17 00:00:00 2001 From: Guillermo Villafuerte Date: Thu, 28 Dec 2023 01:19:01 +0000 Subject: [PATCH 04/39] Translated using Weblate (Spanish) Currently translated at 100.0% (3 of 3 strings) Translation: Kvaesitso/Plugin SDK Translate-URL: https://i18n.mm20.de/projects/kvaesitso/plugins/plugin-sdk/es/ --- plugins/sdk/src/main/res/values-es/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/sdk/src/main/res/values-es/strings.xml b/plugins/sdk/src/main/res/values-es/strings.xml index a6b3daec..9d1057e1 100644 --- a/plugins/sdk/src/main/res/values-es/strings.xml +++ b/plugins/sdk/src/main/res/values-es/strings.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + %1$s solicita acceso a los datos de %2$s. + Denegar + Permitir + \ No newline at end of file From 35ff1bce1aba915e3ae11f6c4d529641a6d2afa0 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 29 Dec 2023 17:50:55 +0100 Subject: [PATCH 05/39] Add weather plugin specific settings --- .../settings/plugins/PluginSettingsScreen.kt | 71 ++++++++++++++++++- .../plugins/PluginSettingsScreenVM.kt | 12 ++++ .../WeatherIntegrationSettingsScreen.kt | 33 +++++++++ .../WeatherIntegrationSettingsScreenVM.kt | 8 +++ core/i18n/src/main/res/values/strings.xml | 7 ++ .../mm20/launcher2/database/daos/PluginDao.kt | 2 +- .../data/plugins/PluginRepositoryImpl.kt | 2 +- .../mm20/launcher2/plugins/PluginService.kt | 15 ++++ 8 files changed, 145 insertions(+), 5 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt index 8028ba29..1c7c389d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.InsertDriveFile +import androidx.compose.material.icons.automirrored.rounded.OpenInNew import androidx.compose.material.icons.rounded.Delete import androidx.compose.material.icons.rounded.Error import androidx.compose.material.icons.rounded.Info @@ -40,6 +41,7 @@ import androidx.compose.runtime.getValue 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.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -48,7 +50,9 @@ import coil.compose.AsyncImage import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginType +import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.Banner +import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.SwitchPreference import de.mm20.launcher2.ui.locals.LocalNavController @@ -76,6 +80,11 @@ fun PluginSettingsScreen(pluginId: String) { minActiveState = Lifecycle.State.RESUMED ) + val weatherPlugins by viewModel.weatherPlugins.collectAsStateWithLifecycle( + emptyList(), + minActiveState = Lifecycle.State.RESUMED + ) + val requestPermissionStarter = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { @@ -87,6 +96,10 @@ fun PluginSettingsScreen(pluginId: String) { null ) + val weatherProviderId by viewModel.weatherProvider.collectAsStateWithLifecycle( + null + ) + Scaffold( topBar = { TopAppBar( @@ -286,7 +299,7 @@ fun PluginSettingsScreen(pluginId: String) { AnimatedVisibility(pluginPackage?.enabled == true && hasPermission == true) { if (filePlugins.isNotEmpty()) { PreferenceCategory( - "File search", + stringResource(R.string.plugin_type_filesearch), iconPadding = false, ) { for (plugin in filePlugins) { @@ -294,7 +307,7 @@ fun PluginSettingsScreen(pluginId: String) { if (state is PluginState.SetupRequired) { Banner( modifier = Modifier.padding(16.dp), - text = state.message ?: "You need to setup this plugin first", + text = state.message ?: stringResource(R.string.plugin_state_setup_required), icon = Icons.Rounded.Info, primaryAction = { TextButton(onClick = { @@ -311,7 +324,7 @@ fun PluginSettingsScreen(pluginId: String) { } else if (state is PluginState.Error) { Banner( modifier = Modifier.padding(16.dp), - text = "This plugin isn't working correctly", + text = stringResource(R.string.plugin_state_error), icon = Icons.Rounded.Error, color = MaterialTheme.colorScheme.errorContainer, ) @@ -334,6 +347,58 @@ fun PluginSettingsScreen(pluginId: String) { } } } + if (weatherPlugins.isNotEmpty()) { + PreferenceCategory( + stringResource(R.string.plugin_type_weather), + iconPadding = false, + ) { + for (plugin in weatherPlugins) { + val state = plugin.state + if (state is PluginState.SetupRequired) { + Banner( + modifier = Modifier.padding(16.dp), + text = state.message ?: stringResource(R.string.plugin_state_setup_required), + icon = Icons.Rounded.Info, + primaryAction = { + TextButton(onClick = { + try { + state.setupActivity.send() + } catch (e: PendingIntent.CanceledException) { + CrashReporter.logException(e) + } + }) { + Text(stringResource(R.string.plugin_action_setup)) + } + } + ) + } else if (state is PluginState.Error) { + Banner( + modifier = Modifier.padding(16.dp), + text = stringResource(R.string.plugin_state_error), + icon = Icons.Rounded.Error, + color = MaterialTheme.colorScheme.errorContainer, + ) + } + Preference( + title = plugin.plugin.label, + enabled = state is PluginState.Ready && weatherProviderId != plugin.plugin.authority, + iconPadding = false, + summary = if (weatherProviderId != plugin.plugin.authority) { + stringResource(R.string.plugin_weather_provider_enable) + } else { + stringResource(R.string.plugin_weather_provider_enabled) + } + ) + } + Preference( + title = stringResource(R.string.widget_config_weather_integration_settings), + icon = Icons.AutoMirrored.Rounded.OpenInNew, + onClick = { + navController?.navigate("settings/integrations/weather") + } + ) + } + } } } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt index 036c3b44..c05483a5 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt @@ -14,6 +14,7 @@ import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginWithState +import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -30,6 +31,7 @@ import org.koin.core.component.inject class PluginSettingsScreenVM : ViewModel(), KoinComponent { private val pluginService by inject() private val fileSearchSettings: FileSearchSettings by inject() + private val weatherSettings: WeatherSettings by inject() private var pluginPackageName = MutableStateFlow(null) @@ -70,6 +72,11 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent { it.filter { it.plugin.type == PluginType.FileSearch } } + val weatherPlugins = states + .map { + it.filter { it.plugin.type == PluginType.Weather } + } + fun init(pluginId: String) { this.pluginPackageName.value = pluginId @@ -101,4 +108,9 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent { fun setFileSearchPluginEnabled(authority: String, enabled: Boolean) { fileSearchSettings.setPluginEnabled(authority, enabled) } + + val weatherProvider = weatherSettings.providerId + fun setWeatherProvider(providerId: String) { + weatherSettings.setProviderId(providerId) + } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt index ee787a05..dab574aa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt @@ -1,18 +1,27 @@ package de.mm20.launcher2.ui.settings.weather +import android.app.PendingIntent import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Info +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.ui.BuildConfig import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog +import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.preferences.* import de.mm20.launcher2.weather.WeatherLocation @@ -25,12 +34,36 @@ fun WeatherIntegrationSettingsScreen() { val availableProviders by viewModel.availableProviders.collectAsState(emptyList()) + val pluginState by viewModel.weatherProviderPluginState.collectAsStateWithLifecycle( + null, + minActiveState = Lifecycle.State.RESUMED + ) + PreferenceScreen( title = stringResource(R.string.preference_screen_weatherwidget), helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/integrations/weather" ) { item { PreferenceCategory { + val state = pluginState?.state + if (state is PluginState.SetupRequired) { + Banner( + modifier = Modifier.padding(16.dp), + text = state.message ?: stringResource(R.string.plugin_state_setup_required), + icon = Icons.Rounded.Info, + primaryAction = { + TextButton(onClick = { + try { + state.setupActivity.send() + } catch (e: PendingIntent.CanceledException) { + CrashReporter.logException(e) + } + }) { + Text(stringResource(R.string.plugin_action_setup)) + } + } + ) + } val weatherProvider by viewModel.weatherProvider.collectAsState() ListPreference( title = stringResource(R.string.preference_weather_provider), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt index 0e90ed46..32f28032 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.weather.WeatherLocation import de.mm20.launcher2.weather.WeatherProviderInfo @@ -15,9 +16,11 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMap import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -25,6 +28,7 @@ import org.koin.core.component.inject class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent { private val repository: WeatherRepository by inject() private val weatherSettings: WeatherSettings by inject() + private val pluginService: PluginService by inject() private val permissionsManager: PermissionsManager by inject() private val dataStore: LauncherDataStore by inject() @@ -36,6 +40,10 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent { weatherSettings.setProviderId(provider) } + val weatherProviderPluginState = weatherProvider.flatMapLatest { + it?.let { pluginService.getPluginWithState(it) } ?: flowOf(null) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + val imperialUnits = dataStore.data.map { it.weather.imperialUnits } fun setImperialUnits(imperialUnits: Boolean) { viewModelScope.launch { diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index 42c4e299..a4ec2551 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -852,4 +852,11 @@ Unavailable This shortcut is unavailable because %1$s isn\'t the default launcher + This plugin isn\'t working correctly + You need to setup this plugin first + Set up + File search + Weather provider + Set as weather provider + Currently set as weather provider \ No newline at end of file diff --git a/data/database/src/main/java/de/mm20/launcher2/database/daos/PluginDao.kt b/data/database/src/main/java/de/mm20/launcher2/database/daos/PluginDao.kt index 6e1923eb..68f483ef 100644 --- a/data/database/src/main/java/de/mm20/launcher2/database/daos/PluginDao.kt +++ b/data/database/src/main/java/de/mm20/launcher2/database/daos/PluginDao.kt @@ -25,7 +25,7 @@ interface PluginDao { ): Flow> @Query("SELECT * FROM Plugins WHERE authority = :authority") - fun get(authority: String): Flow + fun get(authority: String): Flow @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertMany(plugins: List) diff --git a/data/plugins/src/main/java/de/mm20/launcher2/data/plugins/PluginRepositoryImpl.kt b/data/plugins/src/main/java/de/mm20/launcher2/data/plugins/PluginRepositoryImpl.kt index 15cc8865..60444aa3 100644 --- a/data/plugins/src/main/java/de/mm20/launcher2/data/plugins/PluginRepositoryImpl.kt +++ b/data/plugins/src/main/java/de/mm20/launcher2/data/plugins/PluginRepositoryImpl.kt @@ -31,7 +31,7 @@ internal class PluginRepositoryImpl( } override fun get(authority: String): Flow { - return dao.get(authority).map { Plugin(it) } + return dao.get(authority).map { it?.let { Plugin(it) } } } override fun insertMany(plugins: List): Job { diff --git a/services/plugins/src/main/java/de/mm20/launcher2/plugins/PluginService.kt b/services/plugins/src/main/java/de/mm20/launcher2/plugins/PluginService.kt index 43b210f4..ed453333 100644 --- a/services/plugins/src/main/java/de/mm20/launcher2/plugins/PluginService.kt +++ b/services/plugins/src/main/java/de/mm20/launcher2/plugins/PluginService.kt @@ -46,6 +46,10 @@ interface PluginService { enabled: Boolean? = null, ): Flow> + fun getPluginWithState( + authority: String, + ): Flow + fun getPluginPackages(): Flow> fun getPluginPackage(packageName: String): Flow suspend fun getPluginState(plugin: Plugin): PluginState? @@ -132,6 +136,17 @@ internal class PluginServiceImpl( } } + override fun getPluginWithState(authority: String): Flow { + return repository.get(authority).map { + it?.let { + PluginWithState( + plugin = it, + state = getPluginState(it), + ) + } + } + } + override suspend fun getPluginState(plugin: Plugin): PluginState { val bundle = try { From 0bfdd1484f59aa087bb45195874bb9cea7f0ab51 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 29 Dec 2023 19:04:16 +0100 Subject: [PATCH 06/39] Fix favorites reorder sheet in RTL languages Fix #637 --- .../ui/component/dragndrop/DragAndDropGrid.kt | 16 +++------------- .../ui/launcher/sheets/EditFavoritesSheet.kt | 12 +++++++----- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/dragndrop/DragAndDropGrid.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/dragndrop/DragAndDropGrid.kt index 97ac2edc..b4398efc 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/dragndrop/DragAndDropGrid.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/dragndrop/DragAndDropGrid.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.* import androidx.compose.ui.zIndex import de.mm20.launcher2.ui.ktx.animateTo @@ -178,7 +177,6 @@ fun LazyVerticalDragAndDropGrid( columns, modifier.dragAndDrop( state, - LocalLayoutDirection.current == LayoutDirection.Rtl, LocalHapticFeedback.current ), state.gridState, @@ -208,7 +206,6 @@ fun LazyHorizontalDragAndDropGrid( rows, modifier.dragAndDrop( state, - LocalLayoutDirection.current == LayoutDirection.Rtl, LocalHapticFeedback.current ), state.gridState, @@ -224,7 +221,6 @@ fun LazyHorizontalDragAndDropGrid( fun Modifier.dragAndDrop( state: LazyDragAndDropGridState, - isRtl: Boolean, hapticFeedback: HapticFeedback ) = this then pointerInput(null) { @@ -235,10 +231,7 @@ fun Modifier.dragAndDrop( onDragStart = { offset -> val draggedItem = state.gridState.layoutInfo.visibleItemsInfo.find { Rect( - it.offset.toOffset().let {off -> - if (isRtl) off.copy(x = state.gridState.layoutInfo.viewportSize.width - off.x - it.size.width) - else off - }, + it.offset.toOffset(), it.size.toSize() ).contains(offset) } @@ -250,10 +243,7 @@ fun Modifier.dragAndDrop( val absPosition = state.draggedItemAbsolutePosition val draggedItem = state.draggedItem if (absPosition != null && draggedItem != null) { - state.draggedItemAbsolutePosition = absPosition + dragAmount.let { - if (isRtl) it.copy(x = -it.x) - else it - } + state.draggedItemAbsolutePosition = absPosition + dragAmount val draggedCenter = Rect(absPosition, draggedItem.size.toSize()).center val dragOver = state.gridState.layoutInfo.visibleItemsInfo.find { Rect( @@ -308,7 +298,7 @@ fun LazyGridItemScope.DraggableItem( modifier = modifier .then(if (isDragged) Modifier else Modifier.animateItemPlacement()) .zIndex(if (isDragged) 1f else 0f) - .offset { + .absoluteOffset { if (state.draggedItem?.key == key) { state.draggedItemOffset?.toIntOffset() ?: IntOffset.Zero } else if (state.droppedItemKey == key) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheet.kt index 50fa96bb..ea4e4ea1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheet.kt @@ -1,8 +1,6 @@ package de.mm20.launcher2.ui.launcher.sheets import android.app.Activity -import android.content.Context -import android.content.pm.LauncherApps import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.IntentSenderRequest @@ -33,7 +31,6 @@ import androidx.compose.material.icons.rounded.Delete import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Tag import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.FilledTonalIconButton @@ -65,12 +62,13 @@ import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize @@ -173,6 +171,7 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: Padding val tagsListState = rememberLazyListState() val tagsTitleSize = 48.dp.toPixels() val tagsSpacing = 12.dp.toPixels() + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl val state = rememberLazyDragAndDropGridState( gridState = gridState, onDragStart = { @@ -200,8 +199,11 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: Padding && hoveredItem.offset.y + tagsTitleSize < position.y ) { val scroll = tagsListState.layoutInfo.viewportStartOffset + val relCenter = + if (isRtl) draggedCenter.copy(x = tagsListState.layoutInfo.viewportSize.width - draggedCenter.x) + else draggedCenter val tag = tagsListState.layoutInfo.visibleItemsInfo.find { - draggedCenter.x + scroll > it.offset && draggedCenter.x + scroll < it.offset + it.size - tagsSpacing + relCenter.x + scroll > it.offset && relCenter.x + scroll < it.offset + it.size - tagsSpacing } hoveredTag = tag?.index?.let { pinnedTags[it].tag } } else { From f2881874a278b50f6ae9b51bf1d5a1b67719604c Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 29 Dec 2023 19:08:15 +0100 Subject: [PATCH 07/39] Fix pager swipe directions in RTL layout --- .../main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 3c5020e9..1bbbd38c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -81,6 +81,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -398,7 +399,7 @@ fun PagerScaffold( .fillMaxSize() .nestedScroll(pagerNestedScrollConnection), beyondBoundsPageCount = 1, - reverseLayout = reverse, + reverseLayout = reverse == (LocalLayoutDirection.current == LayoutDirection.Ltr), state = pagerState, userScrollEnabled = false,//!isWidgetEditMode, flingBehavior = PagerDefaults.flingBehavior( From 921c641cf63695389a877ff6665b209057dded5f Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 29 Dec 2023 19:15:44 +0100 Subject: [PATCH 08/39] Remove deprecated parameter --- .../src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 1bbbd38c..e744a48c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -407,7 +407,6 @@ fun PagerScaffold( lowVelocityAnimationSpec = spring( stiffness = Spring.StiffnessMediumLow, ), - snapVelocityThreshold = 1000.dp, pagerSnapDistance = remember { object : PagerSnapDistance { override fun calculateTargetPage( From 1858c70193bf62dd1f55f2ca230d214956fbcafe Mon Sep 17 00:00:00 2001 From: Guillermo Villafuerte Date: Sat, 30 Dec 2023 16:25:50 -0600 Subject: [PATCH 09/39] MOD: Hide "Edit widgets" when search and apps list is open/shown. Signed-off-by: Guillermo Villafuerte --- .../de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt index 746b58fb..9b2fdaaa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/SearchBarMenu.kt @@ -78,7 +78,8 @@ fun RowScope.SearchBarMenu( } ) val editButton by widgetsVM.editButton.collectAsState() - if (editButton == false) { + val searchOpen by launcherVM.isSearchOpen + if (!searchOpen && editButton == false) { DropdownMenuItem( onClick = { launcherVM.setWidgetEditMode(editMode = true) From 6eb1de49616cd0f68eec846097d83ccd59034767 Mon Sep 17 00:00:00 2001 From: Massimo Pissarello Date: Sun, 31 Dec 2023 06:59:10 +0000 Subject: [PATCH 10/39] Translated using Weblate (Italian) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-it/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/i18n/src/main/res/values-it/strings.xml b/core/i18n/src/main/res/values-it/strings.xml index 5dd8ee34..97549642 100644 --- a/core/i18n/src/main/res/values-it/strings.xml +++ b/core/i18n/src/main/res/values-it/strings.xml @@ -670,4 +670,11 @@ Host del plugin non installato Plugin indicatori Indica con quale plugin è stato creato un risultato di ricerca + Devi prima configurare questo plugin + Imposta + Fornitore meteo + Attualmente impostato come fornitore meteo + Questo plugin non funziona correttamente + Ricerca file + Imposta come fornitore meteo \ No newline at end of file From bf172c276cd7d52e8136d2e86762daaa902a0d3f Mon Sep 17 00:00:00 2001 From: Fjuro Date: Sat, 30 Dec 2023 09:15:37 +0000 Subject: [PATCH 11/39] Translated using Weblate (Czech) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-cs/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/i18n/src/main/res/values-cs/strings.xml b/core/i18n/src/main/res/values-cs/strings.xml index 2148a6cb..252e5ee3 100644 --- a/core/i18n/src/main/res/values-cs/strings.xml +++ b/core/i18n/src/main/res/values-cs/strings.xml @@ -670,4 +670,11 @@ Hostitel doplňků není nainstalován Odznaky doplňků Zobrazit, který doplněk vytvořil výsledek vyhledávání + Nejprve musíte nastavit tento doplněk + Nastavit + Vyhledávání souborů + Poskytovatel počasí + Nastavit jako poskytovatele počasí + Momentálně nastaven jako poskytovatel počasí + Tento doplněk nefunguje tak, jak by měl \ No newline at end of file From 54257ac4a778de94fe0ed1fce3f880a1d6037032 Mon Sep 17 00:00:00 2001 From: Teggol T Date: Wed, 3 Jan 2024 09:30:31 +0000 Subject: [PATCH 12/39] Translated using Weblate (German) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-de/strings.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/i18n/src/main/res/values-de/strings.xml b/core/i18n/src/main/res/values-de/strings.xml index 7df63ee3..c8617221 100644 --- a/core/i18n/src/main/res/values-de/strings.xml +++ b/core/i18n/src/main/res/values-de/strings.xml @@ -665,4 +665,11 @@ Dynamische Zone Plugin-Plaketten Anzeigen, von welchem Plugin ein Suchergebnis stammt + einrichten + Dateisuche + Wetterdienst + Als Wetterdienst gesetzt + Dieses Plugin funktioniert nicht wie erwartet + Sie müssen das Plugin zuerst einrichten + Als Wetterdienst nutzen \ No newline at end of file From c982b9df9b082b2feec2c075f0f8cb744e8db0b7 Mon Sep 17 00:00:00 2001 From: Antonis Tz Date: Tue, 2 Jan 2024 13:54:54 +0000 Subject: [PATCH 13/39] Translated using Weblate (Greek) Currently translated at 84.8% (556 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-el/strings.xml | 89 ++++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/core/i18n/src/main/res/values-el/strings.xml b/core/i18n/src/main/res/values-el/strings.xml index ec2967a5..821e2e5a 100644 --- a/core/i18n/src/main/res/values-el/strings.xml +++ b/core/i18n/src/main/res/values-el/strings.xml @@ -1,6 +1,6 @@ - Αυτόματη εμφάνιση του πληκτρολογίου κατά το άνοιγμα της αναζήτησης + Αυτόματη εμφάνιση του πληκτρολογίου κατά το άνοιγμα του μενού εφαρμογών Απεγκατάσταση Καρφίτσωμα στα αγαπημένα Ξεκαρφίτσωμα @@ -232,7 +232,7 @@ Ρολόι Διάταξη Προεπιλογή - Συμπαγές + Συμπαγής Χρώμα Στυλ Επιλογή ρολογιού @@ -434,7 +434,7 @@ Εισαγωγή ενός αντιγράφου ασφαλείας που δημιουργήθηκε προηγουμένως Φωτεινό Καιρός - Παραμετροποίηση της αναζήτησης + Αναζήτηση, ετικέτες, κρυφά στοιχεία Απόκρυψη γραμμής κατάστασης Γέμισμα ύψους οθόνης Εμφάνιση καρφιτσωμένων και συχνά χρησιμοποιούμενων στοιχείων πάνω από το πλέγμα εφαρμογών @@ -459,7 +459,7 @@ Επιλογή ταπετσαρίας Εμφάνιση ξυπνητηριών που θα χτυπήσουν μέσα στα επόμενα 15 λεπτά Αναζήτηση επαφών σε αυτή τη συσκευή - Αναζήτηση εικονιδίου + Αναζήτηση σε πακέτα εικονιδίων Από %1$s Εμφάνιση σήματος για εφαρμογές με μη αναγνωσμένες ειδοποιήσεις Εξαγωγή και εισαγωγή δεδομένων εφαρμογής εκκίνησης @@ -468,7 +468,7 @@ Το %1$s πρέπει να οριστεί ως προεπιλεγμένη εφαρμογή αρχικής οθόνης για αναζήτηση συντομεύσεων εφαρμογών Εμφάνιση σήματος για εφαρμογές σε αναστολή Αριθμομηχανή - Δεν θα δημιουργηθούν αντίγραφα ασφαλείας για συνδεδεμένους λογαριασμούς και γραφικά στοιχεία τρίτων εφαρμογών. + Δε θα δημιουργηθούν αντίγραφα ασφαλείας για τους συνδεδεμένους λογαριασμούς. Τα γραφικά στοιχεία εφαρμογών τρίτων μπορούν να αποκατασταθούν μόνο σε αυτήν τη συσκευή. Σήματα αρχείων στο cloud Λήψη περιοδικών συναλλαγματικών ισοτιμιών για μετατροπή νομισμάτων Το επιλεγμένο αρχείο δεν φαίνεται να αποτελεί αντίγραφο ασφαλείας. Είστε σίγουροι ότι επιλέξατε το σωστό αρχείο; @@ -492,4 +492,83 @@ Προβολή και εξαγωγή αρχείων καταγραφής Βοήθεια Παράλειψη + Εμφάνιση κουμπιού αποκάλυψης + Τραβήξτε ένα στιγμιότυπο για να αναλύσετε τη χρήση μνήμης. Η εφαρμογή θα μπει σε αδράνεια μέχρι να ολοκληρωθεί αυτή η διαδικασία. + Πάνω + Κέντρο + Κάτω + Ευθυγράμμιση + Ένα γραφικό στοιχείο που αποθηκεύει μία μόνο σημείωση + Μήνυμα + Email + Περισσότερες πληροφορίες + Γράψε μια σημείωση… + Νέα σημείωση + Σημείωση_%1$s + Απόρριψη + Αποθήκευση + Χρήση: 1,5 kg ή 4 cm >> σε + Εφαρμογές πολυμέσων + Πλέγμα, μέγεθος εικονιδίου, πακέτα εικονιδίων, σήματα + Γρήγορες ενέργειες + Διαμορφώστε το στυλ και τα στοιχεία του ρολογιού + Εκκαθαρίστε και δημιουργήστε ξανά την προσωρινή μνήμη του πακέτου εικονιδίων + Εμφάνιση ενός κουμπιού για την αποκάλυψη κρυφών αποτελεσμάτων αναζήτησης + Διαμορφώστε τις γρήγορες ενέργειες και τις συντομεύσεις αναζήτησης + Ο συγκεκριμένος ιστότοπος δεν μπορεί να εισαχθεί αυτόματα ως αναζήτηση ιστού. Μπορείτε να δοκιμάσετε έναν διαφορετικό ιστότοπο ή να εισαγάγετε τα απαιτούμενα δεδομένα με μη αυτόματο τρόπο στο επόμενο βήμα. + Εφαρμογή + Επεξεργασία γρήγορης δράσης + Εισαγάγετε τη διεύθυνση του ιστότοπου: + Το πρότυπο URL που χρησιμοποιείται για τη δημιουργία της διεύθυνσης URL αναζήτησης ιστού. Χρησιμοποιήστε το \"${1}\" ως σύμβολο κράτησης θέσης για τον πραγματικό όρο αναζήτησης, π.χ. https://google.com/search?q=${1}. + Σήματα προσθηκών + Υποδείξτε με ποια προσθήκη δημιουργήθηκε ένα αποτέλεσμα αναζήτησης + Διαχείριση ετικετών και στοιχείων με ετικέτα + Χρωματικοί συνδυασμοί + Επεξεργασία + Απαιτείται άδεια προσθήκης για τη χρήση προσθηκών. + Συμπαγής λειτουργία + Απόκρυψη ωριαίων και ημερήσιων προβλέψεων + Επανεγκατάσταση πακέτων εικονιδίων + Πρόσθετα + Διαχείριση εγκατεστημένων επεκτάσεων + Δεν έχουν εγκατασταθεί πρόσθετα + Δυναμικά χρώματα + Ακτίνα θολώματος + Μέρος του γραφικού στοιχείου ρολογιού που εμφανίζει δυναμικές πληροφορίες (ημερομηνία, αναπαραγωγήςμουσικής, μπαταρία,…) + Ενσωματώσεις + Όλα τα πακέτα εικονιδίων + Προγραμματισμός εκδήλωσης + Τι είδους δράση θέλετε να δημιουργήσετε; + Κλήση + Στιγμιότυπο μνήμης + Λήψη στιγμιότυπου… + Εισαγωγή + %1$s θέμα + Ετικέτα που χρησιμοποιείται για μη έγκυρα γραφικά στοιχεία εφαρμογών + Η σημείωση απορρίφθηκε. + Αντικατάσταση + Δεν μπορείτε να βρείτε τα ημερολόγια σας; + Αρχική οθόνη + Λογαριασμοί + Καιρός + Παλέτα χρωμάτων + Ετικέτες + Ρολόι, γραμμή αναζήτησης, ταπετσαρία, γραμμές συστήματος + Πλέγμα και εικονίδια + Έλεγχος πολυμέσων + Γρήγορες ενέργειες + Δεν έχουν εγκατασταθεί πακέτα εικονιδίων + Βάζω ξυπνητήρι + Διαδικτυακή αναζήτηση + Δημιουργία επαφής + Προβολή ιστότοπου + Αναζήτηση σε έναν ιστότοπο + Αναζήτηση σε μία εφαρμογή + Προσαρμοσμένη πρόθεση + Νέα γρήγορη δράση + Επιλέξτε μια εφαρμογή για αναζήτηση: + Όνομα + Πρότυπο URL + Πειραματικό + Νέα ετικέτα \ No newline at end of file From 4abed9a0ac9046231a761bfb7e45b35aa4c232c9 Mon Sep 17 00:00:00 2001 From: Antonis Tz Date: Tue, 2 Jan 2024 13:30:48 +0000 Subject: [PATCH 14/39] Translated using Weblate (Greek) Currently translated at 100.0% (106 of 106 strings) Translation: Kvaesitso/units --- core/i18n/src/main/res/values-el/units.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/i18n/src/main/res/values-el/units.xml b/core/i18n/src/main/res/values-el/units.xml index df76abab..81196ecd 100644 --- a/core/i18n/src/main/res/values-el/units.xml +++ b/core/i18n/src/main/res/values-el/units.xml @@ -261,4 +261,8 @@ κοντοί τόνοι st. + + πέτρα + πέτρες + \ No newline at end of file From 56a8978c82530800e300698bcba543909eabf8c5 Mon Sep 17 00:00:00 2001 From: Antonis Tz Date: Tue, 2 Jan 2024 13:25:22 +0000 Subject: [PATCH 15/39] Translated using Weblate (Greek) Currently translated at 100.0% (3 of 3 strings) Translation: Kvaesitso/Plugin SDK Translate-URL: https://i18n.mm20.de/projects/kvaesitso/plugins/plugin-sdk/el/ --- plugins/sdk/src/main/res/values-el/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/sdk/src/main/res/values-el/strings.xml b/plugins/sdk/src/main/res/values-el/strings.xml index a6b3daec..c5d5b00e 100644 --- a/plugins/sdk/src/main/res/values-el/strings.xml +++ b/plugins/sdk/src/main/res/values-el/strings.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + %1$s θέλει να αποκτήσει πρόσβαση σε δεδομένα από %2$s. + Άρνηση + Αποδοχή + \ No newline at end of file From 1e8a4e155408ee8bc1100de750354da6cd322cbf Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:15:40 +0100 Subject: [PATCH 16/39] Fix notification badges Close #657 --- .../java/de/mm20/launcher2/badges/Badge.kt | 54 +++++++++++++-- .../de/mm20/launcher2/badges/BadgeService.kt | 69 +++++++------------ .../providers/AppShortcutBadgeProvider.kt | 5 +- .../badges/providers/CloudBadgeProvider.kt | 9 +-- .../providers/NotificationBadgeProvider.kt | 59 ++++++++-------- .../badges/providers/PluginBadgeProvider.kt | 4 +- .../providers/SuspendedAppsBadgeProvider.kt | 10 +-- .../providers/WorkProfileBadgeProvider.kt | 3 +- 8 files changed, 122 insertions(+), 91 deletions(-) diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt index c2cfb6db..a91d0f40 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/Badge.kt @@ -1,10 +1,52 @@ package de.mm20.launcher2.badges import android.graphics.drawable.Drawable +import android.util.Log -data class Badge( - var number: Int? = null, - var progress: Float? = null, - var iconRes: Int? = null, - var icon: Drawable? = null -) \ No newline at end of file +interface Badge { + val number: Int? + val progress: Float? + val iconRes: Int? + val icon: Drawable? +} + +fun Badge( + number: Int? = null, + progress: Float? = null, + iconRes: Int? = null, + icon: Drawable? = null +): Badge = MutableBadge(number, progress, iconRes, icon) + +internal data class MutableBadge( + override var number: Int? = null, + override var progress: Float? = null, + override var iconRes: Int? = null, + override var icon: Drawable? = null +): Badge + +fun Collection.combine(): Badge? { + if (isEmpty()) return null + val badge = MutableBadge() + var progresses = 0 + forEach { + if (it.icon != null && badge.icon == null) badge.icon = it.icon + if (it.iconRes != null && badge.iconRes == null) badge.iconRes = it.iconRes + it.number?.let { a -> + badge.number?.let { b -> badge.number = a + b } ?: run { + badge.number = a + } + } + it.progress?.let { a -> + badge.progress?.let { b -> + badge.progress = a + b + } ?: run { + badge.progress = a + } + progresses++ + } + } + if (progresses > 0) { + badge.progress?.let { badge.progress = it / progresses } + } + return badge +} \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt index 7d1fe414..f31ee849 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt @@ -1,14 +1,29 @@ package de.mm20.launcher2.badges import android.content.Context -import de.mm20.launcher2.badges.providers.* +import de.mm20.launcher2.badges.providers.AppShortcutBadgeProvider +import de.mm20.launcher2.badges.providers.BadgeProvider +import de.mm20.launcher2.badges.providers.CloudBadgeProvider +import de.mm20.launcher2.badges.providers.NotificationBadgeProvider +import de.mm20.launcher2.badges.providers.PluginBadgeProvider +import de.mm20.launcher2.badges.providers.SuspendedAppsBadgeProvider +import de.mm20.launcher2.badges.providers.WorkProfileBadgeProvider import de.mm20.launcher2.badges.settings.BadgeSettings -import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.search.Searchable -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent -import org.koin.core.component.inject interface BadgeService { fun getBadge(searchable: Searchable): Flow @@ -47,44 +62,12 @@ internal class BadgeServiceImpl( } } - override fun getBadge(searchable: Searchable): Flow = channelFlow { - withContext(Dispatchers.Default) { - badgeProviders.collectLatest { providers -> - if (providers.isEmpty()) { - send(null) - return@collectLatest - } - combine(providers.map { it.getBadge(searchable) }) { badges -> - if (badges.all { it == null }) { - return@combine null - } - val badge = Badge() - var progresses = 0 - badges.filterNotNull().forEach { - if (it.icon != null && badge.icon == null) badge.icon = it.icon - if (it.iconRes != null && badge.iconRes == null) badge.iconRes = it.iconRes - it.number?.let { a -> - badge.number?.let { b -> badge.number = a + b } ?: run { - badge.number = a - } - } - it.progress?.let { a -> - badge.progress?.let { b -> - badge.progress = a + b - } ?: run { - badge.progress = a - } - progresses++ - } - } - if (progresses > 0) { - badge.progress?.let { badge.progress = it / progresses } - } - return@combine badge - }.collectLatest { - send(it) - } - } + override fun getBadge(searchable: Searchable): Flow { + return badgeProviders.flatMapLatest { providers -> + if (providers.isEmpty()) return@flatMapLatest flowOf(null) + combine(providers.map { it.getBadge(searchable) }) { it.filterNotNull() } + .map { it.combine() } + .flowOn(Dispatchers.Default) } } diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/AppShortcutBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/AppShortcutBadgeProvider.kt index 127b5afd..b28c0bb3 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/AppShortcutBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/AppShortcutBadgeProvider.kt @@ -3,6 +3,7 @@ package de.mm20.launcher2.badges.providers import android.content.Context import android.content.pm.PackageManager import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.graphics.BadgeDrawable import de.mm20.launcher2.search.AppShortcut import de.mm20.launcher2.search.Searchable @@ -27,7 +28,7 @@ class AppShortcutBadgeProvider( } catch (e: PackageManager.NameNotFoundException) { return@withContext } - val badge = Badge(icon = BadgeDrawable(context, icon)) + val badge = MutableBadge(icon = BadgeDrawable(context, icon)) send(badge) } } else if (packageName != null) { @@ -39,7 +40,7 @@ class AppShortcutBadgeProvider( } catch (e: PackageManager.NameNotFoundException) { return@withContext } - val badge = Badge(icon = BadgeDrawable(context, icon)) + val badge = MutableBadge(icon = BadgeDrawable(context, icon)) send(badge) } } else { diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/CloudBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/CloudBadgeProvider.kt index cafffe20..934aff41 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/CloudBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/CloudBadgeProvider.kt @@ -1,20 +1,21 @@ package de.mm20.launcher2.badges.providers import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.search.File import de.mm20.launcher2.search.Searchable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf class CloudBadgeProvider: BadgeProvider { - override fun getBadge(searchable: Searchable): Flow = flow { + override fun getBadge(searchable: Searchable): Flow { if (searchable is File) { val iconResId = searchable.providerIconRes if (iconResId != null) { - emit(Badge(iconRes = iconResId)) - return@flow + return flowOf(MutableBadge(iconRes = iconResId)) } } - emit(null) + return flowOf(null) } } \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/NotificationBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/NotificationBadgeProvider.kt index 9bf7240b..44ebd880 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/NotificationBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/NotificationBadgeProvider.kt @@ -1,12 +1,13 @@ package de.mm20.launcher2.badges.providers +import android.util.Log import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.notifications.NotificationRepository -import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Application +import de.mm20.launcher2.search.Searchable import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -14,35 +15,33 @@ import org.koin.core.component.inject class NotificationBadgeProvider : BadgeProvider, KoinComponent { private val notificationRepository: NotificationRepository by inject() - override fun getBadge(searchable: Searchable): Flow = channelFlow { - if (searchable is Application) { - val packageName = searchable.componentName.packageName - notificationRepository.notifications.map { - it.filter { it.packageName == packageName } - }.collectLatest { - if (it.isEmpty() || it.none { it.canShowBadge }) { - send(null) - } else { - val badge = Badge( - number = it.sumOf { - if (it.canShowBadge && !it.isGroupSummary) it.number - else 0 - }, - progress = it.mapNotNull { - val progress = it.progress ?: return@mapNotNull null - val progressMax = it.progressMax ?: return@mapNotNull null - return@mapNotNull progress.toFloat() / progressMax.toFloat() + override fun getBadge(searchable: Searchable): Flow { + if (searchable !is Application) return flowOf(null) + + val packageName = searchable.componentName.packageName + return notificationRepository.notifications.map { + it.filter { it.packageName == packageName && it.canShowBadge } + }.map { + if (it.isEmpty()) { + return@map null + } else { + val badge = MutableBadge( + number = it.sumOf { + if (it.canShowBadge && !it.isGroupSummary) it.number + else 0 + }, + progress = it.mapNotNull { + val progress = it.progress ?: return@mapNotNull null + val progressMax = it.progressMax ?: return@mapNotNull null + return@mapNotNull progress.toFloat() / progressMax.toFloat() + } + .takeIf { it.isNotEmpty() } + ?.let { + it.sumOf { it.toDouble() }.toFloat() / it.size } - .takeIf { it.isNotEmpty() } - ?.let { - it.sumOf { it.toDouble() }.toFloat() / it.size - } - ) - send(badge) - } + ) + return@map badge } - } else { - send(null) } } } \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/PluginBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/PluginBadgeProvider.kt index 53962e76..0745e4b0 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/PluginBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/PluginBadgeProvider.kt @@ -2,6 +2,7 @@ package de.mm20.launcher2.badges.providers import android.content.Context import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.Searchable import kotlinx.coroutines.flow.Flow @@ -12,9 +13,10 @@ class PluginBadgeProvider(private val context: Context): BadgeProvider { override fun getBadge(searchable: Searchable): Flow { if (searchable !is SavableSearchable) return flowOf(null) return flow { + emit(null) val icon = searchable.getProviderIcon(context) if (icon != null) { - emit(Badge(icon = icon)) + emit(MutableBadge(icon = icon)) } } } diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/SuspendedAppsBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/SuspendedAppsBadgeProvider.kt index a2c9710f..6dc8495d 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/SuspendedAppsBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/SuspendedAppsBadgeProvider.kt @@ -1,20 +1,22 @@ package de.mm20.launcher2.badges.providers import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.R import de.mm20.launcher2.search.Application import de.mm20.launcher2.search.Searchable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.flowOf import org.koin.core.component.KoinComponent class SuspendedAppsBadgeProvider : BadgeProvider, KoinComponent { - override fun getBadge(searchable: Searchable): Flow = channelFlow { - if (searchable is Application && searchable.isSuspended) { - send(Badge(iconRes = R.drawable.ic_badge_suspended)) + override fun getBadge(searchable: Searchable): Flow { + return if (searchable is Application && searchable.isSuspended) { + flowOf(MutableBadge(iconRes = R.drawable.ic_badge_suspended)) } else { - send(null) + flowOf(null) } } } \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/WorkProfileBadgeProvider.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/WorkProfileBadgeProvider.kt index 99ed08ba..df8e1c96 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/providers/WorkProfileBadgeProvider.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/providers/WorkProfileBadgeProvider.kt @@ -1,6 +1,7 @@ package de.mm20.launcher2.badges.providers import de.mm20.launcher2.badges.Badge +import de.mm20.launcher2.badges.MutableBadge import de.mm20.launcher2.badges.R import de.mm20.launcher2.search.AppProfile import de.mm20.launcher2.search.AppShortcut @@ -13,7 +14,7 @@ class WorkProfileBadgeProvider : BadgeProvider { override fun getBadge(searchable: Searchable): Flow = flow { if (searchable is Application && searchable.profile == AppProfile.Work || searchable is AppShortcut && searchable.profile == AppProfile.Work) { emit( - Badge( + MutableBadge( iconRes = R.drawable.ic_badge_workprofile ) ) From 86e9a53f2bacd757210b7d57c46ef10598a6a60b Mon Sep 17 00:00:00 2001 From: iStuces Date: Thu, 11 Jan 2024 18:35:31 +0000 Subject: [PATCH 17/39] Translated using Weblate (French) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-fr/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/i18n/src/main/res/values-fr/strings.xml b/core/i18n/src/main/res/values-fr/strings.xml index 99edb069..b127c028 100644 --- a/core/i18n/src/main/res/values-fr/strings.xml +++ b/core/i18n/src/main/res/values-fr/strings.xml @@ -698,4 +698,13 @@ Zone dynamique Hôte du plugin non installé Rayon de flou + Vous devez d\'abord configurer ce plugin + Configuration + Recherche de fichier + Provisionneur de météo + Définir en tant que provisionneur météo + Actuellement défini en tant que provisionneur météo + Ce plugin ne fonctionne pas correctement + Insignes de plugin + Indique par quel plugin un résultat de recherche a été créé \ No newline at end of file From 1e4316f895e8206191d3cff80aace557cc484a5b Mon Sep 17 00:00:00 2001 From: v1s7 <86906143+v1s7@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:45:32 +0000 Subject: [PATCH 18/39] Translated using Weblate (Russian) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-ru/strings.xml | 61 +++++++++++++++++--- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/core/i18n/src/main/res/values-ru/strings.xml b/core/i18n/src/main/res/values-ru/strings.xml index 28be78ac..c484ce58 100644 --- a/core/i18n/src/main/res/values-ru/strings.xml +++ b/core/i18n/src/main/res/values-ru/strings.xml @@ -135,7 +135,7 @@ Сильный снегопад Сильный ливень Ливневые дожди и гром - Мокрый снег + Слякоть Дождь Слабый снегопад и гроза Сильные ливни и гроза @@ -229,7 +229,7 @@ Мокрый дождь с грозой Дожди со снегом и гроза Дождь с грозой - Сильный мокрый снег и гроза + Сильная слякоть и гроза Слабые дожди с мокрым снегом Влажность: %1$s Установить местоположение @@ -411,7 +411,7 @@ Веб-сайты Информация о сборке Больше информации об этой сборке приложения - Автоматически открывать клавиатуру при открытии поиска + Автоматически открывать клавиатуру при открытии списка приложений Свайп вправо Двойное касание Длительное нажатие @@ -435,7 +435,7 @@ Жесты Свайп влево Свайп вниз - Ничего + Ничего не делать Открыть поиск Открыть панель уведомлений Жесты и действия @@ -447,13 +447,13 @@ Новое быстрое действие Выберите приложение для поиска: Динамические цвета - Подключенные учетные записи и сторонние виджеты не будут включены в резервную копию. + Подключенные учетные записи не будут включены в резервную копию. Виджеты от сторонних приложений могут быть восстановлены только на этом устройстве. Войти в Nextcloud Owncloud Войдите, для поиска в сервере Nextcloud Войти в Owncloud Войдите, для поиска в сервере Owncloud - Избранные + Быстрый доступ Резервное копирование Экспортируйте и импортируйте данные лаунчера Создать копию @@ -571,7 +571,7 @@ Погода Управление медиа Аккаунты - Найти иконку + Найти иконку в наборе Запускать подсвеченное совпадение или быстрое действие по клавише \"Вперёд\" Запускать по Enter Отсоединить @@ -597,10 +597,10 @@ Искать ярлыки приложений Последняя сохранённая версия: Заметки - Показывать значок для файлов, храняющихся в облачном хранилище + Показывать значок для файлов, хранящихся в облачном хранилище Изменить порядок закрепленных приложений Ошибка при сохранении заметки - Показывать значок, обозначающий, какому приложению принадлежит ярлык + Показывать значок, указывающий, какому приложению принадлежит ярлык Название тега не может быть пустым. Показать управление медиа, если активна медиа-сессия Вычислять математические выражения @@ -639,4 +639,47 @@ Привязанный файл не пустой и его содержимое не совпадает с последней сохранённой версией этой заметки. Какую версию вы хотите оставить\? Оставить выбранную Заметка не может быть записана в привязанный файл. Возможно, он был перемещён или удалён. Копия была сохранена во внутреннем хранилище лаунчера. + Тема %1$s + Заснять срез, чтобы проанализировать использование памяти. Приложение нельзя будет использовать во время этого процесса. + Значки плагина + Указывать, каким плагином был создан результат поиска + Расположить + Веб-поиск + Системный цвет + Из первичного цвета + Палитра + Выбор + Этот ярлык недоступен, потому что %1$s не является главным экраном по умолчанию + Провайдер погоды + В настоящее время назначен провайдером погоды + Сначала вам нужно установить этот плагин + Установить + Вверху + Найти файлы + По центру + Цветовые схемы + Не удалось прочитать выбранный файл. Убедитесь, что вы выбрали корректный файл темы (*.kvtheme), и что файл не поврежден. + Назначить провайдером погоды + Вернуть по умолчанию + Этот плагин не работает правильно + Импорт + Новая цветовая схема + Вы действительно хотите удалить цветовую схему \"%1$s\"? + Срез памяти + Съёмка среза… + Изменить + Цветовая палитра + Для использования плагинов требуется разрешение на них. + Плагины + Управление установленными дополнениями + Установленных плагинов нет + Анимации + Анимация зарядки + Проигрывать анимацию с пузырьками во время зарядки устройства + Хост плагина не установлен + Радиус размытия + Внизу + Динамическая зона + Применить тему + Недоступно \ No newline at end of file From e74b67c833345b8e51781a9cccbfc593eec52b8e Mon Sep 17 00:00:00 2001 From: iStuces Date: Wed, 10 Jan 2024 19:21:10 +0000 Subject: [PATCH 19/39] Translated using Weblate (French) Currently translated at 100.0% (3 of 3 strings) Translation: Kvaesitso/Plugin SDK Translate-URL: https://i18n.mm20.de/projects/kvaesitso/plugins/plugin-sdk/fr/ --- plugins/sdk/src/main/res/values-fr/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/sdk/src/main/res/values-fr/strings.xml b/plugins/sdk/src/main/res/values-fr/strings.xml index a6b3daec..a2dff920 100644 --- a/plugins/sdk/src/main/res/values-fr/strings.xml +++ b/plugins/sdk/src/main/res/values-fr/strings.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + %1$s souhaite accéder aux données de %2$s. + Refuser + Autoriser + \ No newline at end of file From 81b459d73603b3b96cdf0695d44e3d8c7295d189 Mon Sep 17 00:00:00 2001 From: v1s7 <86906143+v1s7@users.noreply.github.com> Date: Thu, 11 Jan 2024 18:54:42 +0000 Subject: [PATCH 20/39] Translated using Weblate (Russian) Currently translated at 100.0% (3 of 3 strings) Translation: Kvaesitso/Plugin SDK Translate-URL: https://i18n.mm20.de/projects/kvaesitso/plugins/plugin-sdk/ru/ --- plugins/sdk/src/main/res/values-ru/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/sdk/src/main/res/values-ru/strings.xml b/plugins/sdk/src/main/res/values-ru/strings.xml index a6b3daec..671f2c1b 100644 --- a/plugins/sdk/src/main/res/values-ru/strings.xml +++ b/plugins/sdk/src/main/res/values-ru/strings.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + %1$s хочет получить доступ к данным из %2$s. + Запретить + Разрешить + \ No newline at end of file From 51aceba8497c8a05959095a68c005a39d267f944 Mon Sep 17 00:00:00 2001 From: Massimo Pissarello Date: Wed, 17 Jan 2024 17:55:33 +0000 Subject: [PATCH 21/39] Translated using Weblate (Italian) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-it/strings.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/i18n/src/main/res/values-it/strings.xml b/core/i18n/src/main/res/values-it/strings.xml index 97549642..e0b67b5b 100644 --- a/core/i18n/src/main/res/values-it/strings.xml +++ b/core/i18n/src/main/res/values-it/strings.xml @@ -5,7 +5,7 @@ Sud sud ovest L\'URL del server non deve essere vuoto Nome utente - Se hai abilitato l\'autenticazione a due fattori, devi utilizzare una password per l\'app qui. + Se hai abilitato l\'autenticazione a due fattori, devi usare una password per l\'app qui. https://it.wikipedia.org Tassi di cambio pubblicati una volta al giorno dalla Banca Centrale Europea. Tutte le informazioni sono fornite \"così come sono\" senza alcun tipo di garanzia. Non si assume alcuna responsabilità per queste informazioni. \n @@ -39,7 +39,7 @@ Compatto Mostra i controlli multimediali quando c\'è una riproduzione attiva Cerca file locali e nel cloud - Utilizzo: 1.5 kg oppure 4 cm >> in + Uso: 1.5 kg oppure 4 cm >> in Non appuntati - usati frequentemente Mostra scorciatoie a diversi motori di ricerca Appuntati: ordinati automaticamente @@ -454,7 +454,7 @@ Imposta %1$s come app home predefinita per creare scorciatoie. Strumenti Pulisci database - Rimuovi le voci interrotte e inutilizzate dal database del launcher + Rimuovi le voci interrotte e inusate dal database del launcher %1$d voce è stata rimossa. %1$d voci sono state rimosse. @@ -468,7 +468,7 @@ Tag… Tag I tag aggiunti verranno visualizzati qui - Gli elementi appuntati e utilizzati di frequente appariranno qui + Gli elementi appuntati e usati di frequente appariranno qui Non ci sono elementi con questo tag Crea tag… application/x-www-form-urlencoded @@ -515,7 +515,7 @@ Nome App Modello dell\'URL - Il modello di URL utilizzato per creare l\'URL della ricerca web. Utilizza \'${1}\' come segnaposto per il termine di ricerca effettivo, ad es. https://google.com/search\?q=${1}. + Il modello di URL usato per creare l\'URL della ricerca web. Usa \'${1}\' come segnaposto per il termine di ricerca effettivo, ad es. https://google.com/search?q=${1}. Maggiori informazioni Sperimentale Aiuto @@ -584,7 +584,7 @@ Variabile Ordinamento risultati ricerca Conteggio avvii - Utilizzo + Uso Integrazioni Schermata home Griglia, dimensione icona, pacchetti icone, indicatori @@ -664,7 +664,7 @@ Questa scorciatoia non è disponibile perché %1$s non è il launcher predefinito Zona dinamica Plugin - Per utilizzare i plugin è necessaria l\'autorizzazione del plugin. + Per usare i plugin è necessaria l\'autorizzazione del plugin. Gestisci estensioni installate Nessun plugin installato Host del plugin non installato From 08aeaa6b8100183f972ec3fe88f40b11a71f88e0 Mon Sep 17 00:00:00 2001 From: affifymirza Date: Thu, 18 Jan 2024 09:29:46 +0000 Subject: [PATCH 22/39] Added translation using Weblate (Malay) --- core/i18n/src/main/res/values-ms/strings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 core/i18n/src/main/res/values-ms/strings.xml diff --git a/core/i18n/src/main/res/values-ms/strings.xml b/core/i18n/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/core/i18n/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From fa6660b3ea4aa82423c8ef239e4989241789b233 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Thu, 18 Jan 2024 07:30:56 +0000 Subject: [PATCH 23/39] Translated using Weblate (Hungarian) Currently translated at 52.6% (345 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-hu/strings.xml | 182 ++++++++++++++++++- 1 file changed, 178 insertions(+), 4 deletions(-) diff --git a/core/i18n/src/main/res/values-hu/strings.xml b/core/i18n/src/main/res/values-hu/strings.xml index f9cb0f83..e3b36e18 100644 --- a/core/i18n/src/main/res/values-hu/strings.xml +++ b/core/i18n/src/main/res/values-hu/strings.xml @@ -176,10 +176,184 @@ \nUtoljára frissítve: %1$s Új esemény Még nem játszottak le médiát - Havas eső - Erős havas eső - Heves esőzés + Havas eső záporok + Havas eső zivatar + Zivatar Enyhe záporok és mennydörgés Gyenge havazás - Erős havas eső és mennydörgés + Havas eső zivatarok és mennydörgés + %1$s téma + Töltés animáció + Méretek: %1$s + A(z) \'${1}\' helyébe a tényleges keresési kifejezés kerül. + A(z) \'${1}\' helyőrző hiányzik ebből az URL-címből + application/x-www-form-urlencoded + + Gyenge eső és mennydörgés + Havas eső és mennydörgés + Enyhe hózáporok és mennydörgés + Kellemes + Zápor + Hózivatar és mennydörgés + Hely kiválasztása + A hely nem található. + Állítsd be a(z) %1$s alkalmazást alapértelmezett kezdőlapként az alkalmazások parancsikonjainak kereséséhez. + Alapértelmezett + A bővítmények használatához bővítményengedély szükséges. + Egyszerű mód + Tematikus ikonok kényszerítése + Óránkénti és napi előrejelzések elrejtése + A médialejátszás vezérléséhez értesítési hozzáférés szükséges + A naptárban való kereséshez szükséges a naptárhoz való hozzáférés engedélyezése. + Pillanatfelvétel készítése a memóriahasználat elemzéséhez. Az alkalmazás lefagy, amíg ez a folyamat be nem fejeződik. + Törölt és nem használt bejegyzések eltávolítása az indító adatbázisból + + %1$d bejegyzés törölve + %1$d bejegyzés törölve + + Csatlakoztatott fiókok és szolgáltatások kezelése + Adj engedélyt a naptárnak a közelgő találkozók és események megjelenítéséhez. + Hózivatarok + Fekete-fehér + Haladó mód + Az alkalmazás színsémájának alkalmazása az összes ikonra, beleértve a nem támogatottakat is (nem ajánlott) + Felhős + Jégeső + Zivatar + Hózáporok és mennydörgés + Enyhe havas eső záporok és mennydörgés + Színösszeállítás + Szél + Hózáporok + Zivatar és mennydörgés + Ehhez a widgethez naptár-engedély szükséges + A(z) %1$s-t alapértelmezett kezdőlapként kell beállítani az alkalmazások parancsikonjainak kereséséhez + Az értesítési jelvények megjelenítéséhez értesítési hozzáférés szükséges + Nyílt forráskódú licencek + Hatszög + Keresés sáv + A helyi fájlok kereséséhez szükség van a külső tárolókhoz való hozzáférés engedélyezésére + Nem találod a naptárakat? + A helyi fájlok kereséséhez szükség van a minden fájl kezelésének engedélyezésére + A névjegyek kereséséhez névjegy hozzáférésre van szükség. + Hibakeresés + Eszközök + Sötét színséma + A kártyák megjelenésének testreszabása + Betűtípus + Rendszer alapértelmezett + Rólunk + Havazás és mennydörgés + Tiszta ég + Havas eső zivatarok + Heves havazás + Köd + Eső és mennydörgés + Havas eső záporok és mennydörgés + Páratartalom: %1$s + Záporok és mennydörgés + Zivatar + Verzió + Rendszer sávok + Elrendezés + Téma + Állapotsori ikonok + Linkek + Navigációs sáv elrejtése + Ikoncsomagok újratelepítése + Az ikoncsomag gyorsítótárának törlése és újraépítése + Színes ikonok az alkalmazás színsémájának megfelelően + Weboldal megnyitása + Alkalmazás és licenc információk + A megjelenés testreszabása + Szolgáltató + Integrációk + MET Norway + OpenWeatherMap + Deutscher Wetterdienst (csak Németország) + HERE + Tartózkodási Hely + Automatikus helymeghatározás + Engedélyezés + Megjelenés + A keresősáv, a widgetek és a keresési eredmények általános elrendezése + Világos + Sötét + Egyedi színséma + Elsődleges + Harmadlagos + Semleges + Semleges Változat + Hiba + Színpaletta + Pillanatfelvétel készítése… + Négyzet + Részben felhős + Enyhe havas eső és mennydörgés + Ismeretlen + Enyhe havas eső záporok + Hózivatar és mennydörgés + Generálás az elsődleges színből + Részletek megjelenítése + Részletek elrejtése + Világos színséma + GPS és helymeghatározó szolgáltatások használata a tartózkodási hely automatikus meghatározásához + Tartózkodási Hely + Használja a Fahrenheit fokot és a mérföld per órát + Birodalmi mértékegységek + Kompakt mód + Memória pillanatkép + Az adatbázis tisztítása + Ikonok + Kártyák + Sarok sugara + Szél: %1$s + Csapadék: %1$s + Az időjárás-adatok nem állnak rendelkezésre. + A hely automatikus meghatározásához helyhozzáférés szükséges + A névjegyek kereséséhez kapcsolattartási engedély szükséges + Stroke szélesség + Átlátszatlanság + Alakzat + Kerekített + Vágott + Alakzat + Rendszer alapértelmezett + Kerekített négyzet + Lekerekített négyzet + Reuleaux háromszög + Kör + Könnycsepp + Kavics + Bővítmények + Telepített kiterjesztések kezelése + Nincsenek telepített bővítmények + Bővítmény hoszt nincs telepítve + Kövesse a rendszert + Tematizált ikonok + Ikoncsomag + Nincs telepített ikoncsomag + Dinamikus színek + Animációk + Buborék animáció lejátszása a készülék töltése közben + Navigációs sáv ikonok + Automatikus + Világos + Sötét + Állapotsor elrejtése + Google + Bejelentkezve %1$s néven + Kijelentkezés + Jelenleg nem vagy bejelentkezve + Ötszög + Háttérkép + Havas eső + Eső + Gyenge havas eső + A naptárban való kereséshez naptár-engedély szükséges + Zivatarok és mennydörgés + Havas eső zivatar és mennydörgés + A fényképek, média és dokumentumok kereséséhez tárhely engedélyre van szükség. + Állítsd be a(z) %1$s alkalmazást alapértelmezett otthoni alkalmazásként a parancsikonok létrehozásához. + Másodlagos \ No newline at end of file From 5d337d3edf248d615022c0d224ca5ac3eb43ce46 Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 18 Jan 2024 09:29:56 +0000 Subject: [PATCH 24/39] Added translation using Weblate (Malay) --- core/i18n/src/main/res/values-ms/units.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 core/i18n/src/main/res/values-ms/units.xml diff --git a/core/i18n/src/main/res/values-ms/units.xml b/core/i18n/src/main/res/values-ms/units.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/core/i18n/src/main/res/values-ms/units.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From d3f04adcf854bd3814ec242c2433270e21c6023d Mon Sep 17 00:00:00 2001 From: Weblate Date: Thu, 18 Jan 2024 09:30:01 +0000 Subject: [PATCH 25/39] Added translation using Weblate (Malay) --- plugins/sdk/src/main/res/values-ms/strings.xml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 plugins/sdk/src/main/res/values-ms/strings.xml diff --git a/plugins/sdk/src/main/res/values-ms/strings.xml b/plugins/sdk/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/plugins/sdk/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file From 8787e82644153fb3562895abfb16b8f879e39d24 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Thu, 18 Jan 2024 13:01:05 +0000 Subject: [PATCH 26/39] Translated using Weblate (Hungarian) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-hu/strings.xml | 322 ++++++++++++++++++- 1 file changed, 319 insertions(+), 3 deletions(-) diff --git a/core/i18n/src/main/res/values-hu/strings.xml b/core/i18n/src/main/res/values-hu/strings.xml index e3b36e18..5711ee57 100644 --- a/core/i18n/src/main/res/values-hu/strings.xml +++ b/core/i18n/src/main/res/values-hu/strings.xml @@ -201,7 +201,7 @@ Alapértelmezett A bővítmények használatához bővítményengedély szükséges. Egyszerű mód - Tematikus ikonok kényszerítése + Témázott ikonok kényszerítése Óránkénti és napi előrejelzések elrejtése A médialejátszás vezérléséhez értesítési hozzáférés szükséges A naptárban való kereséshez szükséges a naptárhoz való hozzáférés engedélyezése. @@ -312,7 +312,7 @@ Az időjárás-adatok nem állnak rendelkezésre. A hely automatikus meghatározásához helyhozzáférés szükséges A névjegyek kereséséhez kapcsolattartási engedély szükséges - Stroke szélesség + Löketszélesség Átlátszatlanság Alakzat Kerekített @@ -330,7 +330,7 @@ Nincsenek telepített bővítmények Bővítmény hoszt nincs telepítve Kövesse a rendszert - Tematizált ikonok + Témázott ikonok Ikoncsomag Nincs telepített ikoncsomag Dinamikus színek @@ -356,4 +356,320 @@ A fényképek, média és dokumentumok kereséséhez tárhely engedélyre van szükség. Állítsd be a(z) %1$s alkalmazást alapértelmezett otthoni alkalmazásként a parancsikonok létrehozásához. Másodlagos + A kiválasztott fájlt nem lehetett beolvasni. Kérjük, győződj meg róla, hogy érvényes témafájlt (*.kvtheme) választottál ki és hogy a fájl nem sérült. + Kinyit gomb megjelenítése + Nincsenek elemek ilyen címkével + Keresés / alkalmazás tár + Médiaalkalmazások + Használat: 1.5 kg vagy 4 cm >> in + A kiemelt egyezés megnyitása, vagy gyors műveletet, ha megérinti az Ugrás gombot + Rejtett keresési eredmények + Minden ikoncsomag + Címkék… + Internetes keresés + + %1$d naptár kiválasztva + %1$d naptár kiválasztva + + Először be kell állítanod ezt a bővítményt + Beállítás + Időjárás szolgáltató + Beállítás időjárás szolgáltatónak + Alulra + Az akkumulátor aktuális töltöttségi szintjének megjelenítése, amikor az akkumulátor lemerült vagy töltődik + Azon riasztások megjelenítése, amelyek a következő 15 percen belül megszólalnak + Naplók + Alkalmazásnaplók megtekintése és exportálása + Google Drive + További információ az alkalmazás ezen verziójáról + Keresés enterrel + Rejtett alkalmazások és keresési eredmények kezelése + Wikipedia URL + Rács, ikonméret, ikoncsomagok, jelvények + Színsémák + Válaszd ki, hogy miről szeretnél biztonsági másolatot készíteni: + Személyes + A kitűzött és gyakran használt elemek itt jelennek meg + Parancsikon létrehozása + Megjelenítés a kedvencekben + Időzítő indítása + Egy ilyen nevű címke már létezik. Ha folytatod, a két címke egyesül. + A címke neve nem lehet üres. + Hozzáadás a névjegyekhez + Fent + A keresési eredmények elrendezése + Ne görgesse ki a keresősávot a látóteréből + Rögzített képernyőforgatás + Jobbra húzás + Keresés megnyitása + Alkalmazás indítása + A képernyő elforgatásának zárolása portré módban + Ehhez a művelethez engedélyezni kell az Kvaesitso kisegítő lehetőségek szolgáltatását. + Magasság + Kiegyensúlyozott + Leválasztás + Maradjon kiválasztva + Összeállítási információk + Hibakeresés + Widgetek konfigurálása + ⚠ Töltés ⚠ + Internetes keresési parancsikonok + Email + Riasztás beállítása + Milyen akciót szeretnél létrehozni? + Több információ + Jobbra húzás + Futó alkalmazások megjelenítése + Értesítési jelvények + Oszlopok száma + Időjárás + Szúrjon be extra helyet az óra fölé, hogy kitöltse a teljes képernyőmagasságot + Parancsikon jelvények + Az alkalmazás nevének megjelenítése az ikon alatt + Az óra stílusának és összetevőinek konfigurálása + Használjon elmosódást a háttérképen + Nem támogatott ezen az eszközön + Háttérkép kiválasztása + Egy jelvény megjelenítése, amely jelzi, hogy egy parancsikon melyik alkalmazáshoz tartozik + Az alkalmazás widgetet nem sikerült betölteni. + Keresés + A Nextcloud szervereden való kereséshez be kell jelentkezned + Zene + Hibaelhárító eszközök + Widgetek + Médiavezérlők megjelenítése, ha aktív médiamunkamenet van + Fájl keresés + Hiba és összeomlás jelentések + Variálható + Valutaváltó + A billentyűzet automatikus megjelenítése az alkalmazásfiók kinyitásakor + Szinkronizálja a widget tartalmát egy külső fájllal + Jelenleg időjárás szolgáltatóként van beállítva + Weboldalak + Egy weboldal előnézetének megjelenítése, ha a keresési lekérdezés URL + Bejelentkezés a Google Drive kereséséhez + A kitűzött elemek sorrendjének módosítása + Egy gomb megjelenítése a widgetek hozzáadásához, eltávolításához és átrendezéséhez + A hivatkozott fájlt nem lehetett beolvasni. Lehetséges, hogy áthelyezték vagy törölték. A másolat visszaállításra került a Kvaesitso belső tárolójából. Ha szerkeszted a jegyzetet, a hivatkozott fájl valószínűleg felülírásra kerül. + Ez a bővítmény nem működik megfelelően + Keresés, címkék, rejtett elemek + A különböző keresőmotorok parancsikonjainak megjelenítése + Keress helyi- és felhőfájlokat + Keresés a(z) %1$s fájljai között + Gyakran használt elemek megjelenítése a kedvencek között + Beállítások + + %1$d kiválasztott elem + %1$d kiválasztott elem + + Balra húzás + Névjegyek keresése ezen az eszközön + Még nem csatlakoztatottál Owncloud fiókot + A biztonsági mentés befejeződött. + Üzenet + Naptárak + Testreszabások + Az egész napos események elrejtése + Billentyűzet megjelenítése + Név + Rögzített keresősáv + Címkék + A csatlakoztatott fiókokról nem készül biztonsági másolat. A harmadik féltől származó alkalmazások widgetjeit csak ezen az eszközön lehet visszaállítani. + Kedvencek & rejtett alkalmazások + Létrehozva a(z) %1$s , a %2$s-n ezzel: %3$s. + Az URL-sablon, amelyet az internetes keresés URL-jének létrehozásához használatos. Használd a ‘${1}’ szót a tényleges keresési kifejezés helyőrzőjeként, pl. https://google.com/search?q=${1}. + + %1$s perc múlva feltöltődik + %1$s perc múlva feltöltődik + + Esemény ütemezése + Add meg a weboldal címét: + Lentről-fölfelé + Ehhez a címkéhez nincsenek elemek hozzárendelve. Ha folytatod, a címke törlődik. + Elemek kiválasztása: + Egy \"%1$s\" gesztust hajtottál végre. Ez a gesztus jelenleg egy \"%2$s\" művelet kiváltására van beállítva. Az akciót azonban a következő okból nem lehetett végrehajtani: + Föntről-lefelé + Lefelé húzás + Kikapcsolási menü megjelenítése + A keresési eredmények sorrendje + Az olvasatlan értesítésekkel rendelkező alkalmazások jelvényének megjelenítése + Keresés egy weboldalon + Keresés egy alkalmazásban + Egyedi szándék + Új gyors művelet + Gyors művelet szerkesztése + Az adott weboldal nem importálható automatikusan webes keresésként. A következő lépésben megpróbálhatsz egy másik webhelyet, vagy manuálisan megadhatod a szükséges adatokat. + Alkalmazás + URL sablon + Kísérleti + Új címke + Bővítmény jelvények + Jelezze, hogy a keresési eredményt melyik bővítmény hozta létre + Telegram csoport + F-Droid tároló + Kényszerített alakzat + Névjegyek + Szerkesztés gomb + Egy gomb megjelenítése a kedvencek átrendezéséhez + Rács & ikonok + Még nem csatlakoztatottál Google fiókot + Fiók csatlakoztatása + Gyors művelet + Beépített widgetek + Válaszd ki, hogy mit szeretnél visszaállítani. A meglévő adatok felülírásra kerülnek! + Keresés az ikoncsomagokban + Nincs telepítve ikoncsomag + Lent + Gesztusok + Gesztusok és gesztusműveletek + Balra húzás + Dupla érintés + Szegély nélküli + Munka + Abc szerint + Indítások száma + Időjárás integráció beállításai + Nem található naptár + Médiavezérlő integráció beállításai + Link a fájlhoz + Rendszer alapértelmezett használata + Az elsődleges színből + Ez a parancsikon nem érhető el, mert a(z) %1$s nem az alapértelmezett indító + Ez a biztonsági mentés a(z) %1$s egy másik verziójával készült. Előfordulhat, hogy egyes adatok nem lesznek helyesen visszaállítva. + Sorok száma + Gyors műveletek + Gyorsműveletek és keresési parancsikonok konfigurálása + Újraválasztás + Ütközés + Ütközések Megoldása + A hivatkozott fájl nem üres, és tartalma nem egyezik a jegyzet utolsó mentett verziójával. Melyik változatot szeretnéd megtartani? + Háttérkép homályosítása + Sötét témák esetén sötétítse a háttérképet + Háttérkép elmosása + Elmosási sugár + Ikonjelvények konfigurálása + A felfüggesztett alkalmazások jelvényeinek megjelenítése + Felfüggesztett alkalmazások + Felhő jelvények + A felhőben tárolt fájlok jelvényének megjelenítése + Bejelentkezés a Nextcloud-ba + Nextcloud + Jelvények + Owncloud + Bejelentkezés az Owncloud-ba + Az Owncloud szervereden való kereséshez be kell jelentkezned + Alkalmazza az alakzatot az összes ikonra, beleértve azokat is, amelyek általában nem támogatnák + Licenc + Ez az alkalmazás egy ingyenes szoftver. + Ikon méret + Cimkék mutatása + Licencelve a GNU General Public License 3.0 alatt + Rács + Óra + Elrendezés + Alapértelmezett + Kompakt + Képernyő magasságának kitöltése + Szín + Stílus + Válassz egy órát + Igazítás + Középre + Felülre + Dinamikus zóna + Dátum + Az aktuális dátum megjelenítése + Dokk + A rögzített elemek első sorának megjelenítése + Média + Akkumulátor + Riasztások + Biztonsági mentés & Visszaállítás + Kvaesitso adatok exportálása és importálása + Biztonsági mentés + A beállítások és az indítóadatok exportálása + Visszaállítás + Importálj egy korábban létrehozott biztonsági másolatot + Összeomlás jelentő + Naplófájl exportálása + Kedvencek + Rögzített és gyakran használt elemek megjelenítése az alkalmazásrács felett + Fájlok + Naptár + Közelgő találkozók és események keresése + Alkalmazás parancsikonok + Alkalmazás parancsikonok keresése + Számológép + Matematikai kifejezések kiértékelése + Mértékegység váltó + Rendszeresen töltse le az árfolyamokat a valuták átváltásához + Wikipedia + Keresés a Wikipedia-n + keresés az interneten + Helyi fájlok + Dokumentumok, fényképek és egyéb, ezen az eszközön tárolt fájlok keresése + Keresés a(z) %1$s fájljai között a Google Drive-on + OneDrive + Keressen a(z) %1$s fájljai között a OneDrive-on + Nextcloud + Owncloud + Naptár + Képek megjelenítése + Jelentősen növeli az adatforgalmat + Bejelentkezés a Google segítségével + Zenei alkalmazások korlátozása + A nem zenei alkalmazások médiaműveleteinek figyelmen kívül hagyása + Stílus + A keresősáv megjelenésének testreszabása + Szín + Egy gomb megjelenítése a rejtett keresési eredmények megjelenítéséhez + Címkék és címkézett elemek kezelése + Gyakran használt + Kezdőképernyő + Óra, keresősáv, háttérkép, rendszersávok + Fiókok + Időjárás + Média vezérlés + Még nem csatlakoztatottál Nextcloud fiókot + A kiválasztott fájl nem tűnik biztonsági mentésnek. Biztos, hogy a megfelelő fájlt választottad ki? + Ez a biztonsági mentés a(z) %1$s egy másik verziójával készült, és ezzel a verzióval nem állítható vissza. + A biztonsági mentés visszaállítása megtörtént. + Ikon kiválasztása + Alapértelmezett + Javaslatok + Kedvencek + Hívás + Weboldal meglátogatása + Válassz egy alkalmazást a kereséshez: + Háttér kártya + Widget konfigurálása + Húzás az átméretezéshez + Összekapcsolva a(z) %1$s-val/vel + Utolsó mentett verzió: + Aktuális fájl tartalma: + Hiba a jegyzet olvasásakor + Hiba a jegyzet mentése közben + A jegyzetet nem lehetett a kapcsolódó fájlba írni. Lehetséges, hogy áthelyezték vagy törölték. A másolat el lett mentve a Kvaesitso belső tárolójába. + Biztosan törölni szeretné a(z) %1$s színsémát? + Új színséma + Egyedi + Paletta + Alapértelmezett visszaállítása + Téma alkalmazása + Nem elérhető + címke szerkesztése + Egy ilyen nevű címke már létezik. + Címke név + Lefelé húzás + Keresősáv pozíciója + Hosszú érintés + Kezdőlap gomb/gesztus + Ne csináljon semmit + Értesítési tár megnyitása + Képernyő kikapcsolása + Gyorsbeállítások megnyitása + A művelet végrehajtásához engedélyezni kell a Kvaesitso kisegítő lehetőségek szolgáltatását. + Használat + Rugalmasság a rangsorolásban + Stabíl \ No newline at end of file From 11fdba915ae233ab0a3a7612327bb07c72eb39cf Mon Sep 17 00:00:00 2001 From: summoner001 Date: Thu, 18 Jan 2024 13:05:14 +0000 Subject: [PATCH 27/39] Translated using Weblate (Hungarian) Currently translated at 100.0% (106 of 106 strings) Translation: Kvaesitso/units --- core/i18n/src/main/res/values-hu/units.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/i18n/src/main/res/values-hu/units.xml b/core/i18n/src/main/res/values-hu/units.xml index 51a16600..a5beb98b 100644 --- a/core/i18n/src/main/res/values-hu/units.xml +++ b/core/i18n/src/main/res/values-hu/units.xml @@ -264,4 +264,5 @@ kilóméter per óra °C + négyzetinch \ No newline at end of file From b531885fec7c8c85556945ea748cd6ade0bdad30 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Thu, 18 Jan 2024 13:06:56 +0000 Subject: [PATCH 28/39] Translated using Weblate (Hungarian) Currently translated at 100.0% (3 of 3 strings) Translation: Kvaesitso/Plugin SDK Translate-URL: https://i18n.mm20.de/projects/kvaesitso/plugins/plugin-sdk/hu/ --- plugins/sdk/src/main/res/values-hu/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/sdk/src/main/res/values-hu/strings.xml b/plugins/sdk/src/main/res/values-hu/strings.xml index a6b3daec..40043904 100644 --- a/plugins/sdk/src/main/res/values-hu/strings.xml +++ b/plugins/sdk/src/main/res/values-hu/strings.xml @@ -1,2 +1,6 @@ - \ No newline at end of file + + A(z) %1$s hozzá szeretne férni a(z) %2$s adataidhoz. + Elutasítás + Engedélyezés + \ No newline at end of file From 89318c2535255936edaa8dc3277b9a4fbad50389 Mon Sep 17 00:00:00 2001 From: affifymirza Date: Thu, 18 Jan 2024 09:30:29 +0000 Subject: [PATCH 29/39] Translated using Weblate (Malay) Currently translated at 4.4% (29 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-ms/strings.xml | 33 +++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/core/i18n/src/main/res/values-ms/strings.xml b/core/i18n/src/main/res/values-ms/strings.xml index a6b3daec..8b9f55a5 100644 --- a/core/i18n/src/main/res/values-ms/strings.xml +++ b/core/i18n/src/main/res/values-ms/strings.xml @@ -1,2 +1,33 @@ - \ No newline at end of file + + Kongsi + Fail pakej + Nyahpasang + Sematkan pada kegemaran + Nyahpin + Balik + Maklumat Applikasi + %1$s pautan + Buka + Ubahsuai + Buka + Sorok + Paparkan + %1$s telah disorokkan. + Kembali + Import + Padam + Buang + Carian + Versi %1$s + Buka dalam apl kalender + Buka dalam apl kenalan + Tetapan + Bantuan + Kertas Dinding + Timur Laut + Timur utara timur + Utara + Timur + Utara utara timur + \ No newline at end of file From 83ede95e039760a6c86e10baa820924bc51d6672 Mon Sep 17 00:00:00 2001 From: summoner001 Date: Fri, 19 Jan 2024 08:03:15 +0000 Subject: [PATCH 30/39] Translated using Weblate (Hungarian) Currently translated at 100.0% (655 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-hu/strings.xml | 94 ++++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/core/i18n/src/main/res/values-hu/strings.xml b/core/i18n/src/main/res/values-hu/strings.xml index 5711ee57..a3cde797 100644 --- a/core/i18n/src/main/res/values-hu/strings.xml +++ b/core/i18n/src/main/res/values-hu/strings.xml @@ -24,8 +24,8 @@ %1$s hivatkozás Megnyitás Megnyitás a naptár alkalmazásban - észak - északkelet + Észak + Északkelet Forrás: Wikipedia Cím: %1$s Előadó: %1$s @@ -55,9 +55,9 @@ E-book Rajz %1$s fájl - A(z) %1$s fájl véglegesen törlve lesz. Folytassa\? - A(z) %1$s parancsikon véglegesen törölve lesz. Folytassa\? - Nem sikerült a(z) %1$s megnyitása + A(z) %1$s fájl véglegesen törlve lesz. Folytatod? + A(z) %1$s parancsikon véglegesen törölve lesz. Folytatod? + Nem sikerült megnyitni a(z) %1$s-t https://google.com/search\?q=${1} https://play.google.com/store/search\?q=${1} Internetes keresés hozzáadása @@ -72,7 +72,7 @@ Zene Kedvencek Widget hozzáadása - Készítő: %1$s + Készítette: %1$s Telepítés folyamatban... (%1$s) Másolat készítése Kihagyás @@ -87,7 +87,7 @@ Prezentáció Űrlap %1$s biztonsági mentés - A(z) %1$s könyvtár és annak összes tartalma véglegesen törlődik. Folytassa\? + A(z) %1$s könyvtár és annak összes tartalma véglegesen törlődik. Folytatod? Google YouTube https://www.youtube.com/results\?search_query=${1} @@ -97,7 +97,7 @@ Importálás URL-címről Időjárás Továbbiak - Itt nincsenek húsvéti tojások, hacsak nem hoztál magaddal. + Itt nincsenek Easter Egg-ek, hacsak nem hoztál magaddal. Bezárás Tovább Beállítások @@ -124,25 +124,25 @@ Ma nincsenek események Holnap egész nap - %1$s médiát játszik - Könnyű hózáporok + A(z) %1$s médiát játszik le + Enyhe hó záporok Jegyzet - Kérem, hagyja abba, csak az idejét vesztegeti + Kérlek, hagyd abba, csak az idődet vesztegeted Kitűzött - kézzel rendezett Címke készítése… Nextcloud szerver URL A kitűzött címkék itt jelennek meg Felhasználónév - Ha engedélyezte a kétfaktoros hitelesítést, akkor itt egy alkalmazásjelszót kell használnia. + Ha engedélyezted a kétfaktoros hitelesítést, akkor itt egy alkalmazásjelszót kell használnod. A bejelentkezés sikertelen: helytelen felhasználónév vagy jelszó. Owncloud szerver URL Közelgő Következő esemény Mindent mutat Ma - Gyenge esőzés - Enyhe záporok - Könnyű havazás és mennydörgés + Enyhe eső + Enyhe eső záporok + Enyhe havazás és mennydörgés Új jegyzet Mentés Írj egy jegyzetet… @@ -151,7 +151,7 @@ Jegyzet elvetve. Ez az URL nem egy érvényes Nextcloud telepítésre mutat Ismeretlen alkalmazás modul - Nem mondom még egyszer: itt egyáltalán nincsenek elrejtett easter egg-ek + Nem mondom még egyszer: itt egyáltalán nincsenek elrejtett Easter Egg-ek Csere Jogi nyilatkozat Címkék @@ -161,7 +161,7 @@ +%1$d folyamatban lévő esemény az elmúlt napokból Szerkesztés - Húzza ide az elemeket + Húzd ide az elemeket Naptár alkalmazás megnyitása Importálás Kelet @@ -177,24 +177,24 @@ Új esemény Még nem játszottak le médiát Havas eső záporok - Havas eső zivatar - Zivatar - Enyhe záporok és mennydörgés - Gyenge havazás - Havas eső zivatarok és mennydörgés + Heves havas eső + Heves eső + Enyhe eső záporok és mennydörgés + Enyhe havazás + Heves havas eső záporok és mennydörgés %1$s téma Töltés animáció Méretek: %1$s - A(z) \'${1}\' helyébe a tényleges keresési kifejezés kerül. - A(z) \'${1}\' helyőrző hiányzik ebből az URL-címből + A \'${1}\' helyőrzőbe a tényleges keresési kifejezés kerül. + A \'${1}\' helyőrző hiányzik ebből az URL-címből application/x-www-form-urlencoded - Gyenge eső és mennydörgés + Enyhe eső és mennydörgés Havas eső és mennydörgés - Enyhe hózáporok és mennydörgés + Enyhe hó záporok és mennydörgés Kellemes - Zápor - Hózivatar és mennydörgés + Eső záporok + Heves havazás és mennydörgés Hely kiválasztása A hely nem található. Állítsd be a(z) %1$s alkalmazást alapértelmezett kezdőlapként az alkalmazások parancsikonjainak kereséséhez. @@ -213,28 +213,28 @@ Csatlakoztatott fiókok és szolgáltatások kezelése Adj engedélyt a naptárnak a közelgő találkozók és események megjelenítéséhez. - Hózivatarok + Heves hó záporok Fekete-fehér Haladó mód Az alkalmazás színsémájának alkalmazása az összes ikonra, beleértve a nem támogatottakat is (nem ajánlott) Felhős Jégeső Zivatar - Hózáporok és mennydörgés + Hó záporok és mennydörgés Enyhe havas eső záporok és mennydörgés Színösszeállítás Szél - Hózáporok - Zivatar és mennydörgés + Hó záporok + Heves eső és mennydörgés Ehhez a widgethez naptár-engedély szükséges A(z) %1$s-t alapértelmezett kezdőlapként kell beállítani az alkalmazások parancsikonjainak kereséséhez Az értesítési jelvények megjelenítéséhez értesítési hozzáférés szükséges Nyílt forráskódú licencek Hatszög Keresés sáv - A helyi fájlok kereséséhez szükség van a külső tárolókhoz való hozzáférés engedélyezésére + A helyi fájlok kereséséhez, tárhely engedélyre van szükség Nem találod a naptárakat? - A helyi fájlok kereséséhez szükség van a minden fájl kezelésének engedélyezésére + A helyi fájlok kereséséhez, összes fájl kezelése engedélyre van szükség A névjegyek kereséséhez névjegy hozzáférésre van szükség. Hibakeresés Eszközök @@ -245,14 +245,14 @@ Rólunk Havazás és mennydörgés Tiszta ég - Havas eső zivatarok + Heves havas eső záporok Heves havazás Köd Eső és mennydörgés Havas eső záporok és mennydörgés Páratartalom: %1$s - Záporok és mennydörgés - Zivatar + Eső záporok és mennydörgés + Heves eső záporok Verzió Rendszer sávok Elrendezés @@ -292,7 +292,7 @@ Enyhe havas eső és mennydörgés Ismeretlen Enyhe havas eső záporok - Hózivatar és mennydörgés + Heves hó záporok és mennydörgés Generálás az elsődleges színből Részletek megjelenítése Részletek elrejtése @@ -311,12 +311,12 @@ Csapadék: %1$s Az időjárás-adatok nem állnak rendelkezésre. A hely automatikus meghatározásához helyhozzáférés szükséges - A névjegyek kereséséhez kapcsolattartási engedély szükséges + A névjegyek kereséséhez, névjegy engedélyre van szükség Löketszélesség Átlátszatlanság Alakzat Kerekített - Vágott + Csapott Alakzat Rendszer alapértelmezett Kerekített négyzet @@ -329,7 +329,7 @@ Telepített kiterjesztések kezelése Nincsenek telepített bővítmények Bővítmény hoszt nincs telepítve - Kövesse a rendszert + Kövesse a rendszer beállításait Témázott ikonok Ikoncsomag Nincs telepített ikoncsomag @@ -349,10 +349,10 @@ Háttérkép Havas eső Eső - Gyenge havas eső - A naptárban való kereséshez naptár-engedély szükséges - Zivatarok és mennydörgés - Havas eső zivatar és mennydörgés + Enyhe havas eső + A naptárban való kereséshez, naptár engedélyre van szükség + Heves eső záporok és mennydörgés + Heves havas eső és mennydörgés A fényképek, média és dokumentumok kereséséhez tárhely engedélyre van szükség. Állítsd be a(z) %1$s alkalmazást alapértelmezett otthoni alkalmazásként a parancsikonok létrehozásához. Másodlagos @@ -361,7 +361,7 @@ Nincsenek elemek ilyen címkével Keresés / alkalmazás tár Médiaalkalmazások - Használat: 1.5 kg vagy 4 cm >> in + Használat: 1.5 kg, vagy 4 cm >> in A kiemelt egyezés megnyitása, vagy gyors műveletet, ha megérinti az Ugrás gombot Rejtett keresési eredmények Minden ikoncsomag @@ -412,7 +412,7 @@ Összeállítási információk Hibakeresés Widgetek konfigurálása - ⚠ Töltés ⚠ + ⚠⚡︎ Töltés ⚡︎⚠ Internetes keresési parancsikonok Email Riasztás beállítása From 75fa90c204dcebd137af89bf3a6a302905fd49af Mon Sep 17 00:00:00 2001 From: affifymirza Date: Fri, 19 Jan 2024 01:43:45 +0000 Subject: [PATCH 31/39] Translated using Weblate (Malay) Currently translated at 13.2% (87 of 655 strings) Translation: Kvaesitso/i18n --- core/i18n/src/main/res/values-ms/strings.xml | 67 +++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/core/i18n/src/main/res/values-ms/strings.xml b/core/i18n/src/main/res/values-ms/strings.xml index 8b9f55a5..8ab67332 100644 --- a/core/i18n/src/main/res/values-ms/strings.xml +++ b/core/i18n/src/main/res/values-ms/strings.xml @@ -26,8 +26,71 @@ Bantuan Kertas Dinding Timur Laut - Timur utara timur + Timur-timur laut Utara Timur - Utara utara timur + Utara timur laut + Timur tenggara + Tenggara + Selatan tenggara + Selatan + Selatan barat daya + Barat daya + Barat-barat daya + Barat + Barat-barat laut + Barat laut + Utara barat laut + Tiada hujan + Daripada Wikipedia + Tajuk: %1$s + Artis: %1$s + Album: %1$s + Tempoh: %1$s + Tahun: %1$s + Saiz: %1$s + Jenis: %1$s + Nama apl: %1$s + Versi: %1$s + Nama pakej: %1$s + Versi SDK min: %1$s + Pemilik: %1$s + Lokasi: %1$s + Direktori + Fail pakej android + Sumber fail kod + Dokumen + Lampiran + Fail muzik + Gambar + Persembahan + Fail termampat + E-buku + Lukisan + Borang + %1$s tema + %1$s fail + Fail %1$s akan dipadamkan secara kekal. Teruskan? + Google + YouTube + https://www.youtube.com/results?search_query=${1} + Google Play + Tambah carian web + Sunting carian sesawang + Nama + URL + Direktori %1$s dan semua kandungannya akan dipadamkan secara kekal. Teruskan? + Pintasan %1$s akan dibuang secara kekal. Teruskan? + Laluan: %1$s + https://google.com/search?q=${1} + https://ms.wikipedia.org + Video + Fail teks + Fail arkib + Fail + \'${1}\' akan digantikan dengan istilah carian sebenar. + Dimensi: %1$s + %1$s salinan sandar + Tidak dapat membuka %1$s + https://play.google.com/store/search?q=${1} \ No newline at end of file From 92ddc750600c59ccf37a5ccd0cab90d9c0ee4b38 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:29:37 +0100 Subject: [PATCH 32/39] Refactor preferences module --- .../ui/assistant/AssistantScaffold.kt | 6 +- .../mm20/launcher2/ui/base/ProvideSettings.kt | 30 +- .../mm20/launcher2/ui/common/FavoritesVM.kt | 29 +- .../launcher2/ui/common/ImportThemeSheetVM.kt | 15 +- .../launcher2/ui/common/SearchablePickerVM.kt | 11 +- .../common/WeatherLocationSearchDialogVM.kt | 4 +- .../mm20/launcher2/ui/component/SearchBar.kt | 12 +- .../ui/component/ShapedLauncherIcon.kt | 5 +- .../ui/launcher/LauncherScaffoldVM.kt | 132 +++---- .../launcher2/ui/launcher/PagerScaffold.kt | 2 +- .../launcher2/ui/launcher/PullDownScaffold.kt | 4 +- .../ui/launcher/SharedLauncherActivity.kt | 12 +- .../gestures/LauncherGestureHandler.kt | 4 +- .../launcher2/ui/launcher/search/SearchVM.kt | 93 ++--- .../search/favorites/SearchFavoritesVM.kt | 18 +- .../launcher/searchbar/LauncherSearchBar.kt | 4 +- .../launcher/sheets/EditFavoritesSheetVM.kt | 32 +- .../ui/launcher/sheets/FailedGestureSheet.kt | 15 +- .../launcher/sheets/FailedGestureSheetVM.kt | 28 +- .../ui/launcher/widgets/WidgetsVM.kt | 7 +- .../ui/launcher/widgets/clock/ClockWidget.kt | 93 +++-- .../launcher/widgets/clock/ClockWidgetVM.kt | 27 +- .../widgets/clock/WatchFaceSelector.kt | 38 +- .../widgets/clock/clocks/AnalogClock.kt | 6 +- .../widgets/clock/clocks/BinaryClock.kt | 5 +- .../widgets/clock/clocks/DigitalClock1.kt | 16 +- .../widgets/clock/clocks/DigitalClock2.kt | 3 +- .../widgets/clock/clocks/OrbitClock.kt | 6 +- .../widgets/clock/parts/AlarmPartProvider.kt | 5 +- .../clock/parts/BatteryPartProvider.kt | 8 +- .../widgets/clock/parts/DatePartProvider.kt | 5 +- .../clock/parts/FavoritesPartProvider.kt | 18 +- .../widgets/clock/parts/MusicPartProvider.kt | 7 +- .../widgets/clock/parts/PartProvider.kt | 3 +- .../widgets/favorites/FavoritesWidget.kt | 5 + .../widgets/favorites/FavoritesWidgetVM.kt | 53 +-- .../widgets/weather/WeatherWidgetVM.kt | 8 +- .../launcher2/ui/locals/CompositionLocals.kt | 8 +- .../launcher2/ui/settings/SettingsActivity.kt | 4 - .../appearance/AppearanceSettingsScreen.kt | 21 +- .../appearance/AppearanceSettingsScreenVM.kt | 40 +-- .../buildinfo/BuildInfoSettingsScreenVM.kt | 1 - .../ui/settings/cards/CardsSettingsScreen.kt | 25 +- .../settings/cards/CardsSettingsScreenVM.kt | 63 +--- .../clockwidget/ClockWidgetSettingsScreen.kt | 204 ----------- .../ClockWidgetSettingsScreenVM.kt | 139 ++------ .../colorscheme/ThemesSettingsScreenVM.kt | 35 +- .../easteregg/EasterEggSettingsScreenVM.kt | 14 +- .../favorites/FavoritesSettingsScreen.kt | 2 +- .../favorites/FavoritesSettingsScreenVM.kt | 60 +--- .../filesearch/FileSearchSettingsScreenVM.kt | 2 +- .../gestures/GestureSettingsScreen.kt | 51 +-- .../gestures/GestureSettingsScreenVM.kt | 188 +++------- .../HiddenItemsSettingsScreenVM.kt | 14 +- .../homescreen/HomescreenSettingsScreen.kt | 32 +- .../homescreen/HomescreenSettingsScreenVM.kt | 203 +++-------- .../ui/settings/icons/IconsSettingsScreen.kt | 73 ++-- .../settings/icons/IconsSettingsScreenVM.kt | 128 ++----- .../media/MediaIntegrationSettingsScreenVM.kt | 27 +- .../plugins/PluginSettingsScreenVM.kt | 6 +- .../settings/search/SearchSettingsScreen.kt | 7 +- .../settings/search/SearchSettingsScreenVM.kt | 153 +++----- .../UnitConverterSettingsScreenVM.kt | 30 +- .../WeatherIntegrationSettingsScreen.kt | 1 - .../WeatherIntegrationSettingsScreenVM.kt | 23 +- .../wikipedia/WikipediaSettingsScreen.kt | 12 +- .../wikipedia/WikipediaSettingsScreenVM.kt | 45 +-- .../mm20/launcher2/ui/theme/LauncherTheme.kt | 45 +-- .../launcher2/ui/theme/colorscheme/Custom.kt | 4 +- core/preferences/build.gradle.kts | 2 + core/preferences/consumer-rules.pro | 4 +- .../mm20/launcher2/preferences/DataStore.kt | 48 --- .../de/mm20/launcher2/preferences/Defaults.kt | 106 +++--- .../launcher2/preferences/ImportExport.kt | 12 +- .../preferences/LauncherDataStore.kt | 23 ++ .../preferences/LauncherSettingsData.kt | 332 +++++++++++++++++ .../LauncherSettingsDataSerializer.kt | 23 +- .../launcher2/preferences/LegacyDataStore.kt | 52 +++ .../de/mm20/launcher2/preferences/Module.kt | 44 ++- .../preferences/SettingsSerializer.kt | 24 -- .../mm20/launcher2/preferences/ktx/Scheme.kt | 6 +- .../preferences/media/MediaSettings.kt | 31 ++ .../migrations/FactorySettingsMigration.kt | 8 +- .../preferences/migrations/Migration1.kt | 254 +++++++++++++ .../preferences/migrations/Migration_10_11.kt | 4 +- .../preferences/migrations/Migration_11_12.kt | 8 +- .../preferences/migrations/Migration_12_13.kt | 6 +- .../preferences/migrations/Migration_13_14.kt | 4 +- .../preferences/migrations/Migration_14_15.kt | 4 +- .../preferences/migrations/Migration_15_16.kt | 6 +- .../preferences/migrations/Migration_16_17.kt | 8 +- .../preferences/migrations/Migration_17_18.kt | 6 +- .../preferences/migrations/Migration_1_2.kt | 4 +- .../preferences/migrations/Migration_2_3.kt | 4 +- .../preferences/migrations/Migration_3_4.kt | 6 +- .../preferences/migrations/Migration_4_5.kt | 4 +- .../preferences/migrations/Migration_5_6.kt | 7 +- .../preferences/migrations/Migration_6_7.kt | 4 +- .../preferences/migrations/Migration_7_8.kt | 4 +- .../preferences/migrations/Migration_8_9.kt | 4 +- .../preferences/migrations/Migration_9_10.kt | 6 +- .../migrations/VersionedMigration.kt | 10 +- .../search/CalculatorSearchSettings.kt | 18 + .../search/CalendarSearchSettings.kt | 17 + .../search/ContactSearchSettings.kt | 15 + .../preferences/search/FavoritesSettings.kt | 44 +++ .../preferences/search/FileSearchSettings.kt | 77 ++++ .../preferences/search/RankingSettings.kt | 19 + .../search/ShortcutSearchSettings.kt | 18 + .../search/UnitConverterSettings.kt | 34 ++ .../search/WebsiteSearchSettings.kt | 17 + .../search/WikipediaSearchSettings.kt | 27 ++ .../launcher2/preferences/ui/BadgeSettings.kt | 71 ++++ .../preferences/ui/ClockWidgetSettings.kt | 107 ++++++ .../preferences/ui/GestureSettings.kt | 87 +++++ .../launcher2/preferences/ui/IconSettings.kt | 62 ++++ .../preferences/ui/SearchUiSettings.kt | 65 ++++ .../launcher2/preferences/ui/UiSettings.kt | 334 ++++++++++++++++++ .../mm20/launcher2/preferences/ui/UiState.kt | 18 + .../preferences/weather/WeatherSettings.kt | 165 +++++++++ .../preferences/src/main/proto/settings.proto | 2 +- data/appshortcuts/build.gradle.kts | 1 + .../appshortcuts/AppShortcutRepository.kt | 83 +++-- .../de/mm20/launcher2/appshortcuts/Module.kt | 4 +- data/calculator/build.gradle.kts | 1 + .../calculator/CalculatorRepository.kt | 19 +- .../de/mm20/launcher2/calculator/Module.kt | 2 +- data/calendar/build.gradle.kts | 1 + .../launcher2/calendar/CalendarRepository.kt | 30 +- .../java/de/mm20/launcher2/calendar/Module.kt | 4 +- data/contacts/build.gradle.kts | 1 + .../launcher2/contacts/ContactRepository.kt | 213 +++++------ .../java/de/mm20/launcher2/contacts/Module.kt | 4 +- .../de/mm20/launcher2/database/AppDatabase.kt | 2 +- .../database/migrations/Migration_24_25.kt | 209 +---------- .../mm20/launcher2/files/FilesRepository.kt | 41 +-- .../java/de/mm20/launcher2/files/Module.kt | 5 - .../files/settings/FileSearchSettings.kt | 100 ------ .../files/settings/migrations/Migration1.kt | 33 -- .../de/mm20/launcher2/searchable/Module.kt | 8 +- .../searchable/SavableSearchableRepository.kt | 10 +- data/themes/build.gradle.kts | 1 + .../mm20/launcher2/themes/ThemeRepository.kt | 13 +- .../de/mm20/launcher2/unitconverter/Module.kt | 2 +- .../unitconverter/UnitConverterRepository.kt | 41 ++- .../weather/GeocoderWeatherProvider.kt | 1 + .../java/de/mm20/launcher2/weather/Module.kt | 4 - .../mm20/launcher2/weather/WeatherLocation.kt | 14 - .../mm20/launcher2/weather/WeatherProvider.kt | 1 + .../launcher2/weather/WeatherRepository.kt | 12 +- .../weather/brightsky/BrightSkyProvider.kt | 2 +- .../launcher2/weather/here/HereProvider.kt | 2 +- .../launcher2/weather/metno/MetNoProvider.kt | 6 +- .../openweathermap/OpenWeatherMapProvider.kt | 2 +- .../weather/plugin/PluginWeatherProvider.kt | 2 +- .../weather/settings/WeatherSettings.kt | 119 ------- .../weather/settings/WeatherSettingsData.kt | 63 ---- data/websites/build.gradle.kts | 1 + .../java/de/mm20/launcher2/websites/Module.kt | 2 +- .../launcher2/websites/WebsiteRepository.kt | 28 +- .../mm20/launcher2/widgets/FavoritesWidget.kt | 1 + .../wikipedia/WikipediaRepository.kt | 43 ++- .../de/mm20/launcher2/badges/BadgeService.kt | 4 +- .../java/de/mm20/launcher2/badges/Module.kt | 5 - .../badges/settings/BadgeSettings.kt | 68 ---- .../badges/settings/BadgeSettingsData.kt | 50 --- .../badges/settings/migrations/Migration1.kt | 28 -- .../de/mm20/launcher2/icons/IconService.kt | 10 +- .../java/de/mm20/launcher2/music/Module.kt | 2 +- .../de/mm20/launcher2/music/MusicService.kt | 17 +- .../de/mm20/launcher2/search/SearchService.kt | 155 +++----- 171 files changed, 3202 insertions(+), 2962 deletions(-) delete mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt delete mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherDataStore.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt rename data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettingsData.kt => core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsDataSerializer.kt (55%) create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/LegacyDataStore.kt delete mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/SettingsSerializer.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/media/MediaSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration1.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalculatorSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalendarSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ContactSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FavoritesSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FileSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/RankingSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ShortcutSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/UnitConverterSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WebsiteSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WikipediaSearchSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/BadgeSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/GestureSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/IconSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/SearchUiSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiState.kt create mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/weather/WeatherSettings.kt delete mode 100644 data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettings.kt delete mode 100644 data/files/src/main/java/de/mm20/launcher2/files/settings/migrations/Migration1.kt delete mode 100644 data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettings.kt delete mode 100644 data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettingsData.kt delete mode 100644 services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettings.kt delete mode 100644 services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettingsData.kt delete mode 100644 services/badges/src/main/java/de/mm20/launcher2/badges/settings/migrations/Migration1.kt diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt index b433fcef..2ee1002f 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt @@ -19,19 +19,17 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.systemuicontroller.rememberSystemUiController -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.SearchBarColors import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler -import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.searchbar.LauncherSearchBar import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map @Composable fun AssistantScaffold( @@ -204,7 +202,7 @@ fun AssistantScaffold( showHiddenItemsButton = true, value = { value }, onValueChange = { searchVM.search(it) }, - darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == Settings.SearchBarSettings.SearchBarColors.Auto || searchBarColor == Settings.SearchBarSettings.SearchBarColors.Dark, + darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark, style = searchBarStyle, reverse = bottomSearchBar, onKeyboardActionGo = if (launchOnEnter) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt index db9add58..f973ac6d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt @@ -1,9 +1,11 @@ package de.mm20.launcher2.ui.base import androidx.compose.runtime.* -import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.ui.CardStyle +import de.mm20.launcher2.preferences.ui.GridSettings +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.ui.component.ProvideIconShape import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled @@ -19,32 +21,28 @@ import org.koin.androidx.compose.inject fun ProvideSettings( content: @Composable () -> Unit ) { - val dataStore: LauncherDataStore by inject() + val settings: UiSettings by inject() val widgetRepository: WidgetRepository by inject() val cardStyle by remember { - dataStore.data.map { it.cards }.distinctUntilChanged() + settings.cardStyle.distinctUntilChanged() }.collectAsState( - Settings.CardSettings.getDefaultInstance() + CardStyle() ) val iconShape by remember { - dataStore.data.map { - if (it.easterEgg) Settings.IconSettings.IconShape.EasterEgg - else it.icons.shape - }.distinctUntilChanged() - }.collectAsState(Settings.IconSettings.IconShape.Circle) + settings.iconShape.distinctUntilChanged() + }.collectAsState(IconShape.Circle) val favoritesEnabled by remember { combine( widgetRepository.exists(FavoritesWidget.Type), - dataStore.data.map { it.favorites.enabled }, - dataStore.data.map { it.clockWidget.favoritesPart }, - ) { a, b, c -> a || b || c }.distinctUntilChanged() + settings.favoritesEnabled, + ) { a, b -> a || b }.distinctUntilChanged() }.collectAsState(true) val gridSettings by remember { - dataStore.data.map { it.grid }.distinctUntilChanged() - }.collectAsState(Settings.GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build()) + settings.gridSettings.distinctUntilChanged() + }.collectAsState(GridSettings()) CompositionLocalProvider( LocalCardStyle provides cardStyle, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt index bf6a6ae3..b740aa98 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/FavoritesVM.kt @@ -4,7 +4,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.data.customattrs.utils.withCustomLabels -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.FavoritesSettings +import de.mm20.launcher2.preferences.search.FavoritesSettingsData import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.data.Tag import de.mm20.launcher2.services.favorites.FavoritesService @@ -19,11 +20,11 @@ abstract class FavoritesVM : ViewModel(), KoinComponent { private val favoritesService: FavoritesService by inject() internal val widgetRepository: WidgetRepository by inject() private val customAttributesRepository: CustomAttributesRepository by inject() - internal val dataStore: LauncherDataStore by inject() + internal val settings: FavoritesSettings by inject() val selectedTag = MutableStateFlow(null) - val showEditButton = dataStore.data.map { it.favorites.editButton } + val showEditButton = settings.showEditButton abstract val tagsExpanded: Flow val pinnedTags = favoritesService.getFavorites( @@ -36,24 +37,18 @@ abstract class FavoritesVM : ViewModel(), KoinComponent { open val favorites: Flow> = selectedTag.flatMapLatest { tag -> if (tag == null) { - val columns = dataStore.data.map { it.grid.columnCount } val excludeCalendar = widgetRepository.exists(CalendarWidget.Type) - val includeFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed } - val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows } combine( - listOf( - columns, - excludeCalendar, - includeFrequentlyUsed, - frequentlyUsedRows - ) - ) { it }.transformLatest { + excludeCalendar, + settings, + ) { (a, b) -> a as Boolean to b as FavoritesSettingsData } + .transformLatest { - val columns = it[0] as Int - val excludeCalendar = it[1] as Boolean - val includeFrequentlyUsed = it[2] as Boolean - val frequentlyUsedRows = it[3] as Int + val columns = it.second.columns + val excludeCalendar = it.first + val includeFrequentlyUsed = it.second.frequentlyUsed + val frequentlyUsedRows = it.second.frequentlyUsedRows val pinned = favoritesService.getFavorites( excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt index 71e67b3b..f3a6e8c8 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt @@ -5,7 +5,8 @@ import android.net.Uri import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ThemeDescriptor +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.Theme import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.fromJson @@ -17,7 +18,7 @@ import org.koin.core.component.inject class ImportThemeSheetVM : ViewModel(), KoinComponent { private val themeRepository: ThemeRepository by inject() - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() val theme = mutableStateOf(null) val error = mutableStateOf(false) @@ -56,16 +57,10 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent { } } - private suspend fun importTheme(theme: Theme, apply: Boolean) { + private fun importTheme(theme: Theme, apply: Boolean) { themeRepository.createTheme(theme) if (apply) { - dataStore.updateData { - it.toBuilder() - .setAppearance( - it.appearance.toBuilder() - .setThemeId(theme.id.toString()) - ).build() - } + uiSettings.setTheme(ThemeDescriptor.Custom(theme.id.toString())) } } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt index 366aea3f..d6933651 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/SearchablePickerVM.kt @@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.LauncherIcon -import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.toList @@ -15,15 +14,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class SearchablePickerVM: ViewModel(), KoinComponent { +class SearchablePickerVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() private val searchService: SearchService by inject() private val iconService: IconService by inject() @@ -41,15 +38,11 @@ class SearchablePickerVM: ViewModel(), KoinComponent { searchQuery = query searchJob?.cancel() searchJob = viewModelScope.launch { - val settings = dataStore.data.first() searchService.search( query = query, - shortcuts = settings.appShortcutSearch, - contacts = settings.contactsSearch, - calendars = settings.calendarSearch, ).collectLatest { if (searchQuery != query) return@collectLatest - items = withContext(Dispatchers.Default) { + items = withContext(Dispatchers.Default) { it.toList().filterIsInstance().sorted() } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/WeatherLocationSearchDialogVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/WeatherLocationSearchDialogVM.kt index a55172bd..10d44dfa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/WeatherLocationSearchDialogVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/WeatherLocationSearchDialogVM.kt @@ -2,9 +2,9 @@ package de.mm20.launcher2.ui.common import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel -import de.mm20.launcher2.weather.WeatherLocation +import de.mm20.launcher2.preferences.weather.WeatherLocation +import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.weather.WeatherRepository -import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.* import kotlinx.coroutines.flow.first import org.koin.core.component.KoinComponent diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt index 518cb28f..84c2e068 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt @@ -39,7 +39,7 @@ import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.SearchBarStyle import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.layout.BottomReversed import de.mm20.launcher2.ui.locals.LocalCardStyle @@ -47,7 +47,7 @@ import de.mm20.launcher2.ui.locals.LocalCardStyle @Composable fun SearchBar( modifier: Modifier = Modifier, - style: Settings.SearchBarSettings.SearchBarStyle, + style: SearchBarStyle, level: SearchBarLevel, value: String, onValueChange: (String) -> Unit, @@ -78,7 +78,7 @@ fun SearchBar( } ) { when { - it == SearchBarLevel.Resting && style != Settings.SearchBarSettings.SearchBarStyle.Solid -> 0.dp + it == SearchBarLevel.Resting && style != SearchBarStyle.Solid -> 0.dp it == SearchBarLevel.Raised -> 8.dp else -> 2.dp } @@ -98,7 +98,7 @@ fun SearchBar( }) { when { it == SearchBarLevel.Active -> LocalCardStyle.current.opacity - style != Settings.SearchBarSettings.SearchBarStyle.Transparent -> 1f + style != SearchBarStyle.Transparent -> 1f it == SearchBarLevel.Resting -> 0f else -> 1f } @@ -117,14 +117,14 @@ fun SearchBar( } }) { when { - style != Settings.SearchBarSettings.SearchBarStyle.Transparent -> MaterialTheme.colorScheme.onSurface + style != SearchBarStyle.Transparent -> MaterialTheme.colorScheme.onSurface it == SearchBarLevel.Resting -> if (darkColors) Color(0, 0, 0, 180) else Color.White else -> MaterialTheme.colorScheme.onSurface } } val opacity by transition.animateFloat(label = "opacity") { - if (style == Settings.SearchBarSettings.SearchBarStyle.Hidden && it == SearchBarLevel.Resting) 0f + if (style == SearchBarStyle.Hidden && it == SearchBarLevel.Resting) 0f else 1f } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/ShapedLauncherIcon.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/ShapedLauncherIcon.kt index f63cc4f6..fb831220 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/ShapedLauncherIcon.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/ShapedLauncherIcon.kt @@ -15,7 +15,6 @@ import androidx.compose.animation.core.tween import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -57,7 +56,6 @@ import androidx.compose.ui.graphics.drawscope.withTransform import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.graphics.toAndroidRect import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Density @@ -80,7 +78,7 @@ import de.mm20.launcher2.icons.TintedClockLayer import de.mm20.launcher2.icons.TintedIconLayer import de.mm20.launcher2.icons.TransparentLayer import de.mm20.launcher2.ktx.drawWithColorFilter -import de.mm20.launcher2.preferences.Settings.IconSettings.IconShape +import de.mm20.launcher2.preferences.IconShape import de.mm20.launcher2.ui.base.LocalTime import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.locals.LocalDarkTheme @@ -495,7 +493,6 @@ fun getShape(iconShape: IconShape): Shape { IconShape.Teardrop -> TeardropShape IconShape.Pebble -> PebbleShape IconShape.EasterEgg -> EasterEggShape - IconShape.UNRECOGNIZED -> CircleShape } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt index 147e4478..b66da209 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt @@ -12,17 +12,20 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.globalactions.GlobalActionsService import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction -import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.ColorScheme +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.ScreenOrientation +import de.mm20.launcher2.preferences.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarStyle +import de.mm20.launcher2.preferences.ui.GestureSettings +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.ui.gestures.Gesture import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -32,7 +35,8 @@ import org.koin.core.component.inject class LauncherScaffoldVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() + private val gestureSettings: GestureSettings by inject() private val globalActionsService: GlobalActionsService by inject() private val permissionsManager: PermissionsManager by inject() private val searchableRepository: SavableSearchableRepository by inject() @@ -40,40 +44,41 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { private var isSystemInDarkMode = MutableStateFlow(false) private val dimBackgroundState = combine( - dataStore.data.map { it.appearance.dimWallpaper }, - dataStore.data.map { it.appearance.theme }, + uiSettings.dimWallpaper, + uiSettings.colorScheme, isSystemInDarkMode ) { dim, theme, systemDarkMode -> - dim && (theme == Settings.AppearanceSettings.Theme.Dark || theme == Settings.AppearanceSettings.Theme.System && systemDarkMode) + dim && (theme == ColorScheme.Dark || theme == ColorScheme.System && systemDarkMode) } val dimBackground = dimBackgroundState.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - val statusBarColor = dataStore.data.map { it.systemBars.statusBarColor } + val statusBarColor = uiSettings.statusBarColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val navBarColor = dataStore.data.map { it.systemBars.statusBarColor } + val navBarColor = uiSettings.navigationBarColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val chargingAnimation = dataStore.data.map { it.animations.charging } + val chargingAnimation = uiSettings.chargingAnimation .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar } + val hideNavBar = uiSettings.hideNavigationBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar } + val hideStatusBar = uiSettings.hideStatusBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) fun setSystemInDarkMode(darkMode: Boolean) { isSystemInDarkMode.value = darkMode } - val baseLayout = dataStore.data.map { it.layout.baseLayout } + val baseLayout = uiSettings.baseLayout .stateIn(viewModelScope, SharingStarted.Eagerly, null) - val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar } + val bottomSearchBar = uiSettings.bottomSearchBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults } + val reverseSearchResults = uiSettings.reverseSearchResults .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar } + val fixedSearchBar = uiSettings.fixedSearchBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) - val fixedRotation = dataStore.data.map { it.layout.fixedRotation } + val fixedRotation = uiSettings.orientation + .map { it != ScreenOrientation.Auto } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) val isSearchOpen = mutableStateOf(false) @@ -81,8 +86,7 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { val searchBarFocused = mutableStateOf(false) - - val autoFocusSearch = dataStore.data.map { it.searchBar.autoFocus } + val autoFocusSearch = uiSettings.openKeyboardOnSearch fun setSearchbarFocus(focused: Boolean) { if (searchBarFocused.value != focused) searchBarFocused.value = focused @@ -120,45 +124,43 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { isWidgetEditMode.value = editMode } - val wallpaperBlur = dataStore.data.map { it.appearance.blurWallpaper } + val wallpaperBlur = uiSettings.blurWallpaper .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true) - val wallpaperBlurRadius = dataStore.data.map { it.appearance.blurWallpaperRadius } + val wallpaperBlurRadius = uiSettings.wallpaperBlurRadius .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32) - val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight } + val fillClockHeight = uiSettings.clockFillScreen .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true) - val searchBarColor = dataStore.data.map { it.searchBar.color } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarColors.Auto) - val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarStyle.Transparent) + val searchBarColor = uiSettings.searchBarColor + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarColors.Auto) + val searchBarStyle = uiSettings.searchBarStyle + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarStyle.Transparent) - val gestureState: StateFlow = dataStore - .data.map { it.gestures } - .distinctUntilChanged() + val gestureState: StateFlow = gestureSettings .combine(baseLayout) { settings, layout -> val swipeLeftAction = - settings?.swipeLeft?.takeIf { layout != Layout.Pager } ?: GestureAction.None - val swipeRightAction = settings?.swipeRight?.takeIf { layout != Layout.PagerReversed } - ?: GestureAction.None + settings.swipeLeft.takeIf { layout != BaseLayout.Pager } ?: GestureAction.NoAction + val swipeRightAction = settings.swipeRight.takeIf { layout != BaseLayout.PagerReversed } + ?: GestureAction.NoAction val swipeDownAction = - settings?.swipeDown?.takeIf { layout != Layout.PullDown } ?: GestureAction.None - val longPressAction = settings?.longPress ?: GestureAction.None - val doubleTapAction = settings?.doubleTap ?: GestureAction.None - val homeButtonAction = settings?.homeButton ?: GestureAction.None + settings.swipeDown.takeIf { layout != BaseLayout.PullDown } ?: GestureAction.NoAction + val longPressAction = settings.longPress + val doubleTapAction = settings.doubleTap + val homeButtonAction = settings.homeButton val swipeLeftAppKey = - if (swipeLeftAction == GestureAction.LaunchApp) settings.swipeLeftApp else null + if (swipeLeftAction is GestureAction.Launch) swipeLeftAction.key else null val swipeRightAppKey = - if (swipeRightAction == GestureAction.LaunchApp) settings.swipeRightApp else null + if (swipeRightAction is GestureAction.Launch) swipeRightAction.key else null val swipeDownAppKey = - if (swipeDownAction == GestureAction.LaunchApp) settings.swipeDownApp else null + if (swipeDownAction is GestureAction.Launch) swipeDownAction.key else null val longPressAppKey = - if (longPressAction == GestureAction.LaunchApp) settings.longPressApp else null + if (longPressAction is GestureAction.Launch) longPressAction.key else null val doubleTapAppKey = - if (doubleTapAction == GestureAction.LaunchApp) settings.doubleTapApp else null + if (doubleTapAction is GestureAction.Launch) doubleTapAction.key else null val homeButtonAppKey = - if (homeButtonAction == GestureAction.LaunchApp) settings.homeButtonApp else null + if (homeButtonAction is GestureAction.Launch) homeButtonAction.key else null val apps = listOfNotNull( swipeLeftAppKey, swipeRightAppKey, @@ -189,17 +191,17 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { val action = when (gesture) { Gesture.DoubleTap -> gestureState.value.doubleTapAction Gesture.LongPress -> gestureState.value.longPressAction - Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != Layout.PullDown } - Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != Layout.Pager } - Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != Layout.PagerReversed } + Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != BaseLayout.PullDown } + Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != BaseLayout.Pager } + Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != BaseLayout.PagerReversed } Gesture.HomeButton -> gestureState.value.homeButtonAction } val requiresAccessibilityService = - action == GestureAction.OpenRecents - || action == GestureAction.OpenPowerDialog - || action == GestureAction.OpenQuickSettings - || action == GestureAction.OpenNotificationDrawer - || action == GestureAction.LockScreen + action is GestureAction.Recents + || action is GestureAction.PowerMenu + || action is GestureAction.QuickSettings + || action is GestureAction.Notifications + || action is GestureAction.ScreenLock if (action != null && requiresAccessibilityService && !permissionsManager.checkPermissionOnce( PermissionGroup.Accessibility @@ -211,37 +213,37 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { return when (action) { - GestureAction.OpenSearch -> { + is GestureAction.Search -> { openSearch() true } - GestureAction.OpenNotificationDrawer -> { + is GestureAction.Notifications -> { globalActionsService.openNotificationDrawer() true } - GestureAction.OpenQuickSettings -> { + is GestureAction.QuickSettings -> { globalActionsService.openQuickSettings() true } - GestureAction.LockScreen -> { + is GestureAction.ScreenLock -> { globalActionsService.lockScreen() true } - GestureAction.OpenPowerDialog -> { + is GestureAction.PowerMenu -> { globalActionsService.openPowerDialog() true } - GestureAction.OpenRecents -> { + is GestureAction.Recents -> { globalActionsService.openRecents() true } - GestureAction.LaunchApp -> { + is GestureAction.Launch -> { val view = (context as Activity).window.decorView val options = ActivityOptionsCompat.makeScaleUpAnimation( view, @@ -271,12 +273,12 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { } data class GestureState( - val swipeLeftAction: GestureAction = GestureAction.None, - val swipeRightAction: GestureAction = GestureAction.None, - val swipeDownAction: GestureAction = GestureAction.None, - val longPressAction: GestureAction = GestureAction.None, - val doubleTapAction: GestureAction = GestureAction.None, - val homeButtonAction: GestureAction = GestureAction.None, + val swipeLeftAction: GestureAction = GestureAction.NoAction, + val swipeRightAction: GestureAction = GestureAction.NoAction, + val swipeDownAction: GestureAction = GestureAction.NoAction, + val longPressAction: GestureAction = GestureAction.NoAction, + val doubleTapAction: GestureAction = GestureAction.NoAction, + val homeButtonAction: GestureAction = GestureAction.NoAction, val swipeLeftApp: SavableSearchable? = null, val swipeRightApp: SavableSearchable? = null, val swipeDownApp: SavableSearchable? = null, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index e744a48c..7dcc2ac4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -86,7 +86,7 @@ import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.systemuicontroller.rememberSystemUiController -import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarColors import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.SearchBarLevel diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index 251ded4d..9de892c6 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -68,7 +68,7 @@ import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.systemuicontroller.rememberSystemUiController -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.SearchBarColors import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.SearchBarLevel @@ -605,7 +605,7 @@ fun PullDownScaffold( showHiddenItemsButton = isSearchOpen, value = { value }, onValueChange = { searchVM.search(it) }, - darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == Settings.SearchBarSettings.SearchBarColors.Auto || searchBarColor == Settings.SearchBarSettings.SearchBarColors.Dark, + darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark, style = searchBarStyle, reverse = bottomSearchBar, onKeyboardActionGo = if (launchOnEnter) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt index a387a916..5c93c80e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt @@ -34,8 +34,8 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import com.google.accompanist.systemuicontroller.rememberSystemUiController -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.SystemBarsSettings.SystemBarColors +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.SystemBarColors import de.mm20.launcher2.ui.assistant.AssistantScaffold import de.mm20.launcher2.ui.base.BaseActivity import de.mm20.launcher2.ui.base.ProvideCurrentTime @@ -184,7 +184,7 @@ abstract class SharedLauncherActivity( } } else { when (layout) { - Settings.LayoutSettings.Layout.PullDown -> { + BaseLayout.PullDown -> { key(bottomSearchBar, reverseSearchResults) { PullDownScaffold( modifier = Modifier @@ -205,8 +205,8 @@ abstract class SharedLauncherActivity( } } - Settings.LayoutSettings.Layout.Pager, - Settings.LayoutSettings.Layout.PagerReversed -> { + BaseLayout.Pager, + BaseLayout.PagerReversed -> { key(bottomSearchBar, reverseSearchResults) { PagerScaffold( modifier = Modifier @@ -220,7 +220,7 @@ abstract class SharedLauncherActivity( }, darkStatusBarIcons = lightStatus, darkNavBarIcons = lightNav, - reverse = layout == Settings.LayoutSettings.Layout.PagerReversed, + reverse = layout == BaseLayout.PagerReversed, bottomSearchBar = bottomSearchBar, reverseSearchResults = reverseSearchResults, fixedSearchBar = fixedSearchBar, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt index c582988c..05d50058 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.platform.LocalView import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction +import de.mm20.launcher2.preferences.GestureAction import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.ui.component.FakeSplashScreen import de.mm20.launcher2.ui.gestures.Gesture @@ -52,7 +52,7 @@ fun LauncherGestureHandler( val gestureState by viewModel.gestureState.collectAsState(GestureState()) - val shouldDetectDoubleTapGesture = gestureState.doubleTapAction != GestureAction.None + val shouldDetectDoubleTapGesture = gestureState.doubleTapAction !is GestureAction.NoAction LaunchedEffect(shouldDetectDoubleTapGesture) { gestureDetector.shouldDetectDoubleTaps = shouldDetectDoubleTapGesture diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt index ffb3dabc..0e6291cd 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchVM.kt @@ -5,25 +5,30 @@ import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.files.settings.FileSearchSettings -import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.Ordering +import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.Ordering +import de.mm20.launcher2.preferences.SearchResultOrder +import de.mm20.launcher2.preferences.search.CalendarSearchSettings +import de.mm20.launcher2.preferences.search.ContactSearchSettings +import de.mm20.launcher2.preferences.search.FavoritesSettings +import de.mm20.launcher2.preferences.search.FileSearchSettings +import de.mm20.launcher2.preferences.search.ShortcutSearchSettings +import de.mm20.launcher2.preferences.ui.SearchUiSettings import de.mm20.launcher2.search.AppProfile +import de.mm20.launcher2.search.AppShortcut +import de.mm20.launcher2.search.Application +import de.mm20.launcher2.search.Article +import de.mm20.launcher2.search.CalendarEvent import de.mm20.launcher2.search.Contact import de.mm20.launcher2.search.File import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.Searchable -import de.mm20.launcher2.search.AppShortcut -import de.mm20.launcher2.search.Application -import de.mm20.launcher2.search.Article -import de.mm20.launcher2.search.CalendarEvent import de.mm20.launcher2.search.Website import de.mm20.launcher2.search.data.Calculator import de.mm20.launcher2.search.data.UnitConverter +import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.services.favorites.FavoritesService import kotlinx.coroutines.CancellationException @@ -32,7 +37,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn @@ -47,10 +51,14 @@ class SearchVM : ViewModel(), KoinComponent { private val favoritesService: FavoritesService by inject() private val searchableRepository: SavableSearchableRepository by inject() private val permissionsManager: PermissionsManager by inject() - private val dataStore: LauncherDataStore by inject() - private val fileSearchSettings: FileSearchSettings by inject() - val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter } + private val fileSearchSettings: FileSearchSettings by inject() + private val contactSearchSettings: ContactSearchSettings by inject() + private val calendarSearchSettings: CalendarSearchSettings by inject() + private val shortcutSearchSettings: ShortcutSearchSettings by inject() + private val searchUiSettings: SearchUiSettings by inject() + + val launchOnEnter = searchUiSettings.launchOnEnter .stateIn(viewModelScope, SharingStarted.Eagerly, false) private val searchService: SearchService by inject() @@ -70,10 +78,10 @@ class SearchVM : ViewModel(), KoinComponent { val unitConverterResults = mutableStateOf>(emptyList()) val searchActionResults = mutableStateOf>(emptyList()) - val hiddenResultsButton = dataStore.data.map { it.searchBar.hiddenItemsButton } + val hiddenResultsButton = searchUiSettings.hiddenItemsButton val hiddenResults = mutableStateOf>(emptyList()) - val favoritesEnabled = dataStore.data.map { it.favorites.enabled } + val favoritesEnabled = searchUiSettings.favorites val hideFavorites = mutableStateOf(false) private val hiddenItemKeys = searchableRepository @@ -117,16 +125,9 @@ class SearchVM : ViewModel(), KoinComponent { } hideFavorites.value = query.isNotEmpty() searchJob = viewModelScope.launch { - dataStore.data.collectLatest { settings -> + searchUiSettings.resultOrder.collectLatest { resultOrder -> searchService.search( - query, - calculator = settings.calculatorSearch, - unitConverter = settings.unitConverterSearch, - calendars = settings.calendarSearch, - contacts = settings.contactsSearch, - shortcuts = settings.appShortcutSearch, - websites = settings.websiteSearch, - wikipedia = settings.wikipediaSearch, + query ).collectLatest { results -> var resultsList = withContext(Dispatchers.Default) { listOfNotNull( @@ -151,13 +152,13 @@ class SearchVM : ViewModel(), KoinComponent { emptyList() } else { val keys = resultsList.mapNotNull { (it as? SavableSearchable)?.key } - when (settings.resultOrdering.ordering) { + when (resultOrder) { - Ordering.LaunchCount -> searchableRepository.sortByRelevance( + SearchResultOrder.LaunchCount -> searchableRepository.sortByRelevance( keys ).first() - Ordering.Weighted -> searchableRepository.sortByWeight( + SearchResultOrder.Weighted -> searchableRepository.sortByWeight( keys ).first() @@ -256,7 +257,7 @@ class SearchVM : ViewModel(), KoinComponent { val missingCalendarPermission = combine( permissionsManager.hasPermission(PermissionGroup.Calendar), - dataStore.data.map { it.calendarSearch.enabled }.distinctUntilChanged() + calendarSearchSettings.enabled, ) { perm, enabled -> !perm && enabled } fun requestCalendarPermission(context: AppCompatActivity) { @@ -264,18 +265,12 @@ class SearchVM : ViewModel(), KoinComponent { } fun disableCalendarSearch() { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setCalendarSearch(it.calendarSearch.toBuilder().setEnabled(false)) - .build() - } - } + calendarSearchSettings.setEnabled(false) } val missingContactsPermission = combine( permissionsManager.hasPermission(PermissionGroup.Contacts), - dataStore.data.map { it.contactsSearch.enabled }.distinctUntilChanged() + contactSearchSettings.enabled ) { perm, enabled -> !perm && enabled } fun requestContactsPermission(context: AppCompatActivity) { @@ -283,18 +278,12 @@ class SearchVM : ViewModel(), KoinComponent { } fun disableContactsSearch() { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setContactsSearch(it.contactsSearch.toBuilder().setEnabled(false)) - .build() - } - } + contactSearchSettings.setEnabled(false) } val missingFilesPermission = combine( permissionsManager.hasPermission(PermissionGroup.ExternalStorage), - fileSearchSettings.localFiles.distinctUntilChanged() + fileSearchSettings.localFiles ) { perm, enabled -> !perm && enabled } fun requestFilesPermission(context: AppCompatActivity) { @@ -302,18 +291,12 @@ class SearchVM : ViewModel(), KoinComponent { } fun disableFilesSearch() { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFileSearch(it.fileSearch.toBuilder().setLocalFiles(false)) - .build() - } - } + fileSearchSettings.setLocalFiles(false) } val missingAppShortcutPermission = combine( permissionsManager.hasPermission(PermissionGroup.AppShortcuts), - dataStore.data.map { it.appShortcutSearch.enabled }.distinctUntilChanged() + shortcutSearchSettings.enabled, ) { perm, enabled -> !perm && enabled } fun requestAppShortcutPermission(context: AppCompatActivity) { @@ -321,12 +304,6 @@ class SearchVM : ViewModel(), KoinComponent { } fun disableAppShortcutSearch() { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppShortcutSearch(it.appShortcutSearch.toBuilder().setEnabled(false)) - .build() - } - } + shortcutSearchSettings.setEnabled(false) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt index 717b89ca..599e9bbe 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt @@ -1,29 +1,23 @@ package de.mm20.launcher2.ui.launcher.search.favorites import androidx.lifecycle.viewModelScope +import de.mm20.launcher2.preferences.ui.UiState import de.mm20.launcher2.ui.common.FavoritesVM import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch +import org.koin.core.component.inject class SearchFavoritesVM : FavoritesVM() { - override val tagsExpanded: Flow = dataStore.data.map { it.ui.searchTagsMultiline } + private val uiState: UiState by inject() + + override val tagsExpanded: Flow = uiState.favoritesTagsExpanded .shareIn(viewModelScope, SharingStarted.Lazily) override fun setTagsExpanded(expanded: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setUi( - it.ui.toBuilder() - .setSearchTagsMultiline(expanded) - .build() - ) - .build() - } - } + uiState.setFavoritesTagsExpanded(expanded) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt index 8764192e..532ae5f3 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/searchbar/LauncherSearchBar.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalFocusManager import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.SearchBarStyle import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.component.SearchBar import de.mm20.launcher2.ui.component.SearchBarLevel @@ -29,7 +29,7 @@ import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager @Composable fun LauncherSearchBar( modifier: Modifier = Modifier, - style: Settings.SearchBarSettings.SearchBarStyle, + style: SearchBarStyle, level: () -> SearchBarLevel, value: () -> String, onValueChange: (String) -> Unit, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheetVM.kt index 74043535..ea907ae2 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/EditFavoritesSheetVM.kt @@ -17,9 +17,9 @@ import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.normalize import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.appshortcuts.AppShortcut +import de.mm20.launcher2.preferences.search.FavoritesSettings import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.data.Tag import de.mm20.launcher2.services.favorites.FavoritesService @@ -41,7 +41,7 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent { private val badgeService: BadgeService by inject() private val customAttributesRepository: CustomAttributesRepository by inject() private val permissionsManager: PermissionsManager by inject() - private val dataStore: LauncherDataStore by inject() + private val favoritesSettings: FavoritesSettings by inject() val gridItems = mutableStateOf>(emptyList()) @@ -245,36 +245,16 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent { } } - val enableFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed } + val enableFrequentlyUsed = favoritesSettings.frequentlyUsed .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setFrequentlyUsed(frequentlyUsed: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFavorites( - it.favorites - .toBuilder() - .setFrequentlyUsed(frequentlyUsed) - ) - .build() - } - } + favoritesSettings.setFrequentlyUsed(frequentlyUsed) } - val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows } + val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) fun setFrequentlyUsedRows(frequentlyUsedRows: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFavorites( - it.favorites - .toBuilder() - .setFrequentlyUsedRows(frequentlyUsedRows) - ) - .build() - } - } + favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows) } fun pinTag(tag: Tag) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt index ed720fbb..f119e7e8 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt @@ -14,7 +14,8 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.LegacySettings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.MissingPermissionBanner @@ -29,12 +30,12 @@ fun FailedGestureSheet( val viewModel: FailedGestureSheetVM = viewModel() val actionName = stringResource(when(failedGesture.action) { - Settings.GestureSettings.GestureAction.OpenSearch -> R.string.gesture_action_open_search - Settings.GestureSettings.GestureAction.OpenNotificationDrawer -> R.string.gesture_action_notifications - Settings.GestureSettings.GestureAction.LockScreen -> R.string.gesture_action_lock_screen - Settings.GestureSettings.GestureAction.OpenQuickSettings -> R.string.gesture_action_quick_settings - Settings.GestureSettings.GestureAction.OpenRecents -> R.string.gesture_action_recents - Settings.GestureSettings.GestureAction.OpenPowerDialog -> R.string.gesture_action_power_menu + is GestureAction.Search -> R.string.gesture_action_open_search + is GestureAction.Notifications -> R.string.gesture_action_notifications + is GestureAction.ScreenLock -> R.string.gesture_action_lock_screen + is GestureAction.QuickSettings -> R.string.gesture_action_quick_settings + is GestureAction.Recents -> R.string.gesture_action_recents + is GestureAction.PowerMenu -> R.string.gesture_action_power_menu else -> R.string.gesture_action_none }) val gestureName = stringResource(when(failedGesture.gesture) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt index ecaa6db0..6cf11829 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.ui.GestureSettings import de.mm20.launcher2.ui.gestures.Gesture import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -14,28 +14,20 @@ import org.koin.core.component.inject class FailedGestureSheetVM : ViewModel(), KoinComponent { private val permissionsManager: PermissionsManager by inject() - private val dataStore: LauncherDataStore by inject() + private val gestureSettings: GestureSettings by inject() fun requestPermission(context: AppCompatActivity) { permissionsManager.requestPermission(context, PermissionGroup.Accessibility) } fun disableGesture(gesture: Gesture) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures( - it.gestures.toBuilder().apply { - when (gesture) { - Gesture.SwipeDown -> swipeDown = GestureAction.None - Gesture.SwipeLeft -> swipeLeft = GestureAction.None - Gesture.SwipeRight -> swipeRight = GestureAction.None - Gesture.DoubleTap -> doubleTap = GestureAction.None - Gesture.LongPress -> longPress = GestureAction.None - Gesture.HomeButton -> homeButton = GestureAction.None - } - }.build() - ).build() - } + when(gesture) { + Gesture.DoubleTap -> gestureSettings.setDoubleTap(GestureAction.NoAction) + Gesture.LongPress -> gestureSettings.setLongPress(GestureAction.NoAction) + Gesture.SwipeDown -> gestureSettings.setSwipeDown(GestureAction.NoAction) + Gesture.SwipeLeft -> gestureSettings.setSwipeLeft(GestureAction.NoAction) + Gesture.SwipeRight -> gestureSettings.setSwipeRight(GestureAction.NoAction) + Gesture.HomeButton -> gestureSettings.setHomeButton(GestureAction.NoAction) } } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt index 6820cedd..79d51e49 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetsVM.kt @@ -1,9 +1,8 @@ package de.mm20.launcher2.ui.launcher.widgets -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.widgets.Widget import de.mm20.launcher2.widgets.WidgetRepository import kotlinx.coroutines.flow.SharingStarted @@ -15,9 +14,9 @@ import org.koin.core.component.inject class WidgetsVM : ViewModel(), KoinComponent { private val widgetRepository: WidgetRepository by inject() - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() - val editButton = dataStore.data.map { it.widgets.editButton } + val editButton = uiSettings.widgetEditButton .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) val widgets = widgetRepository.get() diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt index 9e53ab7d..f82b92c1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidget.kt @@ -14,8 +14,6 @@ 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.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -64,10 +62,9 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetAlignment -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout +import de.mm20.launcher2.preferences.ClockWidgetAlignment +import de.mm20.launcher2.preferences.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.base.LocalTime import de.mm20.launcher2.ui.component.BottomSheetDialog @@ -89,7 +86,7 @@ fun ClockWidget( ) { val viewModel: ClockWidgetVM = viewModel() val context = LocalContext.current - val layout by viewModel.layout.collectAsState() + val compact by viewModel.compactLayout.collectAsState() val clockStyle by viewModel.clockStyle.collectAsState() val color by viewModel.color.collectAsState() val alignment by viewModel.alignment.collectAsState() @@ -106,7 +103,9 @@ fun ClockWidget( viewModel.updateTime(time) } - val partProvider by remember { viewModel.getActivePart(context) }.collectAsStateWithLifecycle(null) + val partProvider by remember { viewModel.getActivePart(context) }.collectAsStateWithLifecycle( + null + ) AnimatedContent(editMode, label = "ClockWidget") { if (it) { @@ -165,32 +164,32 @@ fun ClockWidget( CompositionLocalProvider( LocalContentColor provides contentColor ) { - if (layout == ClockWidgetLayout.Vertical) { + if (compact == false) { Column( horizontalAlignment = Alignment.CenterHorizontally, ) { Box( modifier = Modifier.clickable( - enabled = clockStyle != ClockStyle.EmptyClock, + enabled = clockStyle !is ClockWidgetStyle.Empty, indication = null, interactionSource = remember { MutableInteractionSource() } ) { viewModel.launchClockApp(context) } ) { - Clock(clockStyle, ClockWidgetLayout.Vertical) + Clock(clockStyle, false) } if (partProvider != null) { DynamicZone( modifier = Modifier.padding(bottom = 8.dp), - layout = ClockWidgetLayout.Vertical, + compact = false, provider = partProvider, ) } } } - if (layout == ClockWidgetLayout.Horizontal) { + if (compact == true) { Row( modifier = Modifier .fillMaxWidth() @@ -201,7 +200,7 @@ fun ClockWidget( if (partProvider != null) { DynamicZone( modifier = Modifier.weight(1f), - layout = ClockWidgetLayout.Horizontal, + compact = true, provider = partProvider, ) } @@ -216,14 +215,14 @@ fun ClockWidget( ) Box( modifier = Modifier.clickable( - enabled = clockStyle != ClockStyle.EmptyClock, + enabled = clockStyle !is ClockWidgetStyle.Empty, indication = null, interactionSource = remember { MutableInteractionSource() } ) { viewModel.launchClockApp(context) } ) { - Clock(clockStyle, ClockWidgetLayout.Horizontal) + Clock(clockStyle, true) } } } @@ -236,7 +235,7 @@ fun ClockWidget( .fillMaxWidth() .padding(bottom = 16.dp) ) { - dockProvider?.Component(ClockWidgetLayout.Vertical) + dockProvider?.Component(false) } } } @@ -246,21 +245,18 @@ fun ClockWidget( @Composable fun Clock( - style: ClockStyle?, - layout: ClockWidgetLayout + style: ClockWidgetStyle?, + compact: Boolean, ) { val time = LocalTime.current when (style) { - ClockStyle.DigitalClock1, - ClockStyle.DigitalClock1_Outlined, - ClockStyle.DigitalClock1_MDY, - ClockStyle.DigitalClock1_OnePlus -> DigitalClock1(time, layout, style) + is ClockWidgetStyle.Digital1 -> DigitalClock1(time, compact, style) - ClockStyle.DigitalClock2 -> DigitalClock2(time, layout) - ClockStyle.BinaryClock -> BinaryClock(time, layout) - ClockStyle.AnalogClock -> AnalogClock(time, layout) - ClockStyle.OrbitClock -> OrbitClock(time, layout) - ClockStyle.EmptyClock -> {} + is ClockWidgetStyle.Digital2 -> DigitalClock2(time, compact) + is ClockWidgetStyle.Binary -> BinaryClock(time, compact) + is ClockWidgetStyle.Analog -> AnalogClock(time, compact) + is ClockWidgetStyle.Orbit -> OrbitClock(time, compact) + is ClockWidgetStyle.Empty -> {} else -> {} } } @@ -268,13 +264,13 @@ fun Clock( @Composable fun DynamicZone( modifier: Modifier = Modifier, - layout: ClockWidgetLayout, + compact: Boolean, provider: PartProvider?, ) { Column( modifier = modifier ) { - provider?.Component(layout) + provider?.Component(compact) } } @@ -283,18 +279,13 @@ fun ConfigureClockWidgetSheet( onDismiss: () -> Unit, ) { val viewModel: ClockWidgetSettingsScreenVM = viewModel() - val layout by viewModel.layout.collectAsState() + val compact by viewModel.compact.collectAsState() val color by viewModel.color.collectAsState() val style by viewModel.clockStyle.collectAsState() val fillHeight by viewModel.fillHeight.collectAsState() val alignment by viewModel.alignment.collectAsState() - - val date by viewModel.datePart.collectAsState() - val favorites by viewModel.favoritesPart.collectAsState() - val media by viewModel.musicPart.collectAsState() - val alarm by viewModel.alarmPart.collectAsState() - val battery by viewModel.batteryPart.collectAsState() - + val dock by viewModel.dock.collectAsState() + val parts by viewModel.parts.collectAsState() BottomSheetDialog(onDismissRequest = onDismiss) { Column( @@ -307,14 +298,14 @@ fun ConfigureClockWidgetSheet( modifier = Modifier.fillMaxWidth(), ) { SegmentedButton( - selected = layout == ClockWidgetLayout.Vertical, + selected = compact == false, onClick = { - viewModel.setLayout(ClockWidgetLayout.Vertical) + viewModel.setCompact(false) }, shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2), icon = { SegmentedButtonDefaults.Icon( - active = layout == ClockWidgetLayout.Vertical, + active = compact == false, ) { Icon( imageVector = Icons.Rounded.HorizontalSplit, @@ -327,14 +318,14 @@ fun ConfigureClockWidgetSheet( Text(text = stringResource(R.string.preference_clockwidget_layout_vertical)) } SegmentedButton( - selected = layout == ClockWidgetLayout.Horizontal, + selected = compact == true, onClick = { - viewModel.setLayout(ClockWidgetLayout.Horizontal) + viewModel.setCompact(true) }, shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2), icon = { SegmentedButtonDefaults.Icon( - active = layout == ClockWidgetLayout.Horizontal, + active = compact == true, ) { Icon( imageVector = Icons.Rounded.VerticalSplit, @@ -348,9 +339,9 @@ fun ConfigureClockWidgetSheet( } } - if (color != null && layout != null) { + if (color != null && compact != null) { WatchFaceSelector( - layout = layout!!, + compact = compact!!, colors = color!!, selected = style, onSelect = { @@ -487,7 +478,7 @@ fun ConfigureClockWidgetSheet( title = stringResource(R.string.preference_clockwidget_favorites_part), summary = stringResource(R.string.preference_clockwidget_favorites_part_summary), icon = Icons.Rounded.Star, - value = favorites == true, + value = dock == true, onValueChanged = { viewModel.setFavoritesPart(it) } @@ -510,7 +501,7 @@ fun ConfigureClockWidgetSheet( title = stringResource(R.string.preference_clockwidget_date_part), summary = stringResource(R.string.preference_clockwidget_date_part_summary), icon = Icons.Rounded.Today, - value = date == true, + value = parts?.date == true, onValueChanged = { viewModel.setDatePart(it) } @@ -519,7 +510,7 @@ fun ConfigureClockWidgetSheet( title = stringResource(R.string.preference_clockwidget_music_part), summary = stringResource(R.string.preference_clockwidget_music_part_summary), icon = Icons.Rounded.MusicNote, - value = media == true, + value = parts?.music == true, onValueChanged = { viewModel.setMusicPart(it) } @@ -528,7 +519,7 @@ fun ConfigureClockWidgetSheet( title = stringResource(R.string.preference_clockwidget_alarm_part), summary = stringResource(R.string.preference_clockwidget_alarm_part_summary), icon = Icons.Rounded.Alarm, - value = alarm == true, + value = parts?.alarm == true, onValueChanged = { viewModel.setAlarmPart(it) } @@ -537,7 +528,7 @@ fun ConfigureClockWidgetSheet( title = stringResource(R.string.preference_clockwidget_battery_part), summary = stringResource(R.string.preference_clockwidget_battery_part_summary), icon = Icons.Rounded.BatteryFull, - value = battery == true, + value = parts?.battery == true, onValueChanged = { viewModel.setBatteryPart(it) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt index 4a540b9e..f1a59ac5 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/ClockWidgetVM.kt @@ -6,7 +6,7 @@ import android.provider.AlarmClock import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import de.mm20.launcher2.ui.launcher.widgets.clock.parts.AlarmPartProvider import de.mm20.launcher2.ui.launcher.widgets.clock.parts.BatteryPartProvider import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider @@ -18,21 +18,20 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import org.koin.core.component.KoinComponent import org.koin.core.component.inject class ClockWidgetVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val settings: ClockWidgetSettings by inject() - private val partProviders = dataStore.data.map { it.clockWidget }.distinctUntilChanged().map { + private val partProviders = settings.parts.map { val providers = mutableListOf() - if (it.datePart) providers += DatePartProvider() - if (it.musicPart) providers += MusicPartProvider() - if (it.batteryPart) providers += BatteryPartProvider() - if (it.alarmPart) providers += AlarmPartProvider() + if (it.date) providers += DatePartProvider() + if (it.music) providers += MusicPartProvider() + if (it.battery) providers += BatteryPartProvider() + if (it.alarm) providers += AlarmPartProvider() providers }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) @@ -51,19 +50,19 @@ class ClockWidgetVM : ViewModel(), KoinComponent { } } - val layout = dataStore.data.map { it.clockWidget.layout } + val compactLayout = settings.compact .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val clockStyle = dataStore.data.map { it.clockWidget.clockStyle } + val clockStyle = settings.clockStyle .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val color = dataStore.data.map { it.clockWidget.color } + val color = settings.color .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val alignment = dataStore.data.map { it.clockWidget.alignment } + val alignment = settings.alignment .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val dockProvider = dataStore.data - .map { if (it.clockWidget.favoritesPart) FavoritesPartProvider() else null } + val dockProvider = settings.dock + .map { if (it) FavoritesPartProvider() else null } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun updateTime(time: Long) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/WatchFaceSelector.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/WatchFaceSelector.kt index 4523661c..8627cd41 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/WatchFaceSelector.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/WatchFaceSelector.kt @@ -43,19 +43,18 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout +import de.mm20.launcher2.preferences.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import kotlinx.coroutines.launch @Composable fun WatchFaceSelector( - layout: ClockWidgetLayout, + compact: Boolean, colors: ClockWidgetColors, - selected: ClockStyle?, - onSelect: (ClockStyle) -> Unit, + selected: ClockWidgetStyle?, + onSelect: (ClockWidgetStyle) -> Unit, ) { val context = LocalContext.current Surface( @@ -73,7 +72,7 @@ fun WatchFaceSelector( modifier = Modifier, ) { val styles = remember { - sortedMapOf( + mapOf( ClockStyle.DigitalClock1 to 0, ClockStyle.DigitalClock1_Outlined to 0, ClockStyle.DigitalClock1_MDY to 0, @@ -159,9 +158,9 @@ fun WatchFaceSelector( styles.filter { it.value == pageIndex } } if (currentPageStyles.containsKey(selected)) { - Clock(selected, layout) + Clock(selected, compact) } else { - Clock(currentPageStyles.keys.first(), layout) + Clock(currentPageStyles.keys.first(), compact) } } } @@ -253,7 +252,7 @@ fun WatchFaceSelector( } } -fun getClockstyleName(context: Context, style: ClockStyle): String { +fun getClockstyleName(context: Context, style: ClockWidgetStyle): String { return when (style) { ClockStyle.DigitalClock1, ClockStyle.DigitalClock1_Outlined, @@ -264,11 +263,11 @@ fun getClockstyleName(context: Context, style: ClockStyle): String { ClockStyle.BinaryClock -> "Binary" ClockStyle.AnalogClock -> "Hands" ClockStyle.EmptyClock -> "Empty" - ClockStyle.UNRECOGNIZED -> "" + else -> "" } } -fun getVariantName(context: Context, style: ClockStyle): String { +fun getVariantName(context: Context, style: ClockWidgetStyle): String { return when (style) { ClockStyle.DigitalClock1, ClockStyle.DigitalClock2, @@ -279,7 +278,20 @@ fun getVariantName(context: Context, style: ClockStyle): String { ClockStyle.DigitalClock1_Outlined -> "Outlined" ClockStyle.DigitalClock1_MDY -> "Material You" ClockStyle.DigitalClock1_OnePlus -> "OnePlus" + else -> "" - ClockStyle.UNRECOGNIZED -> "" } +} + +// Compat for old enum names, TODO refactor this screen +object ClockStyle { + val DigitalClock1 = ClockWidgetStyle.Digital1() + val DigitalClock1_Outlined = ClockWidgetStyle.Digital1(outlined = true) + val DigitalClock1_MDY = ClockWidgetStyle.Digital1(variant = ClockWidgetStyle.Digital1.Variant.MDY) + val DigitalClock1_OnePlus = ClockWidgetStyle.Digital1(variant = ClockWidgetStyle.Digital1.Variant.OnePlus) + val DigitalClock2 = ClockWidgetStyle.Digital2 + val OrbitClock = ClockWidgetStyle.Orbit + val AnalogClock = ClockWidgetStyle.Analog + val BinaryClock = ClockWidgetStyle.Binary + val EmptyClock = ClockWidgetStyle.Empty } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/AnalogClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/AnalogClock.kt index 33557dd5..716faa91 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/AnalogClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/AnalogClock.kt @@ -11,15 +11,15 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.Fill import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import java.util.* @Composable fun AnalogClock( time: Long, - layout: Settings.ClockWidgetSettings.ClockWidgetLayout + compact: Boolean, ) { - val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical + val verticalLayout = !compact val date = Calendar.getInstance() date.timeInMillis = time val minute = date[Calendar.MINUTE] diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt index 2ae7c767..235d9562 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/BinaryClock.kt @@ -7,15 +7,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.Settings import java.util.* @Composable fun BinaryClock( time: Long, - layout: Settings.ClockWidgetSettings.ClockWidgetLayout + compact: Boolean, ) { - val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical + val verticalLayout = !compact val date = Calendar.getInstance() date.timeInMillis = time val minute = date[Calendar.MINUTE] diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt index 27addd4c..4c8efc70 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock1.kt @@ -8,7 +8,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawStyle import androidx.compose.ui.graphics.drawscope.Fill import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.platform.LocalContext @@ -20,8 +19,7 @@ import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle +import de.mm20.launcher2.preferences.ClockWidgetStyle import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.locals.LocalDarkTheme import java.text.SimpleDateFormat @@ -30,10 +28,10 @@ import java.util.* @Composable fun DigitalClock1( time: Long, - layout: Settings.ClockWidgetSettings.ClockWidgetLayout, - variant: ClockStyle = ClockStyle.DigitalClock1, + compact: Boolean, + style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(), ) { - val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical + val verticalLayout = !compact val format = SimpleDateFormat( if (verticalLayout) { if (DateFormat.is24HourFormat(LocalContext.current)) "HH\nmm" else "hh\nmm" @@ -50,8 +48,8 @@ fun DigitalClock1( fontWeight = FontWeight.Black, textAlign = TextAlign.Center, lineHeight = 0.8.em, - drawStyle = if (variant == ClockStyle.DigitalClock1_Outlined) Stroke(width = 2.dp.toPixels()) else Fill, - color = if (variant == ClockStyle.DigitalClock1_MDY) { + drawStyle = if (style.outlined) Stroke(width = 2.dp.toPixels()) else Fill, + color = if (style.variant == ClockWidgetStyle.Digital1.Variant.MDY) { if (LocalContentColor.current == Color.White) { if (LocalDarkTheme.current) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.primaryContainer @@ -64,7 +62,7 @@ fun DigitalClock1( val modifier = Modifier.offset(0.dp, if (verticalLayout) 16.dp else 0.dp) - if (variant == ClockStyle.DigitalClock1_OnePlus) { + if (style.variant == ClockWidgetStyle.Digital1.Variant.OnePlus) { val hour = formattedString.substring(0, 2) Text( modifier = modifier, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt index 6f8c4964..2ae619ba 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/DigitalClock2.kt @@ -6,13 +6,12 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight -import de.mm20.launcher2.preferences.Settings @Composable fun DigitalClock2( time: Long, - layout: Settings.ClockWidgetSettings.ClockWidgetLayout + compact: Boolean, ) { Text( text = DateUtils.formatDateTime(LocalContext.current, time, DateUtils.FORMAT_SHOW_TIME), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt index 6e68390b..72fd823f 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/clocks/OrbitClock.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.unit.center import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toOffset import de.mm20.launcher2.ktx.TWO_PI -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import java.time.Instant import java.time.ZoneId import java.time.ZonedDateTime @@ -46,9 +46,9 @@ private val currentTime @Composable fun OrbitClock( _time: Long, - layout: Settings.ClockWidgetSettings.ClockWidgetLayout + compact: Boolean, ) { - val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical + val verticalLayout = !compact val timeState = remember { mutableStateOf(currentTime) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/AlarmPartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/AlarmPartProvider.kt index e224d55c..7357d992 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/AlarmPartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/AlarmPartProvider.kt @@ -21,7 +21,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.* @@ -71,7 +70,7 @@ class AlarmPartProvider : PartProvider { } @Composable - override fun Component(layout: ClockWidgetLayout) { + override fun Component(compactLayout: Boolean) { val context = LocalContext.current val alarmTime by nextAlarmTime @@ -79,7 +78,7 @@ class AlarmPartProvider : PartProvider { alarmTime?.let { - if (layout == ClockWidgetLayout.Vertical) { + if (!compactLayout) { TextButton( onClick = { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt index 1d09bd5b..9f9e5465 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/BatteryPartProvider.kt @@ -20,14 +20,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService -import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.icons.* import kotlinx.coroutines.channels.awaitClose @@ -54,13 +52,13 @@ class BatteryPartProvider : PartProvider { } @Composable - override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) { + override fun Component(compactLayout: Boolean) { val batteryInfo by this.batteryInfo.collectAsState(null) batteryInfo?.let { - if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) { + if (!compactLayout) { Row( Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically @@ -86,7 +84,7 @@ class BatteryPartProvider : PartProvider { } } } - if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Horizontal) { + if (compactLayout) { Row( Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/DatePartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/DatePartProvider.kt index 6547336a..ee6d40c1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/DatePartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/DatePartProvider.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.em import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.ui.base.LocalTime import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -25,9 +24,9 @@ class DatePartProvider : PartProvider { } @Composable - override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) { + override fun Component(compactLayout: Boolean) { val time = LocalTime.current - val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical + val verticalLayout = !compactLayout val context = LocalContext.current TextButton( colors = ButtonDefaults.textButtonColors( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/FavoritesPartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/FavoritesPartProvider.kt index 570edab9..c58c79b0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/FavoritesPartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/FavoritesPartProvider.kt @@ -3,16 +3,13 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts import android.content.Context import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.widgets.CalendarWidget @@ -27,25 +24,24 @@ class FavoritesPartProvider : PartProvider, KoinComponent { private val favoritesService: FavoritesService by inject() private val widgetRepository: WidgetRepository by inject() - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() override fun getRanking(context: Context): Flow = flow { emit(Int.MAX_VALUE) } @Composable - override fun Component(layout: ClockWidgetLayout) { - val columns by remember(layout) { - dataStore.data.map { - val c = it.grid.columnCount - if (layout == ClockWidgetLayout.Horizontal) c - 2 else c + override fun Component(compactLayout: Boolean) { + val columns by remember { + uiSettings.gridSettings.map { + it.columnCount } }.collectAsState(0) val excludeCalendar by remember { widgetRepository.exists(CalendarWidget.Type) }.collectAsState( true ) - val favorites by remember(columns, excludeCalendar, layout) { + val favorites by remember(columns, excludeCalendar) { favoritesService.getFavorites( excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"), manuallySorted = true, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/MusicPartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/MusicPartProvider.kt index 35c444f8..af2153ee 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/MusicPartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/MusicPartProvider.kt @@ -28,7 +28,6 @@ import androidx.compose.ui.unit.dp import de.mm20.launcher2.music.MusicService import de.mm20.launcher2.music.PlaybackState import de.mm20.launcher2.music.SupportedActions -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout import de.mm20.launcher2.ui.R import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow @@ -48,7 +47,7 @@ class MusicPartProvider : PartProvider, KoinComponent { } @Composable - override fun Component(layout: ClockWidgetLayout) { + override fun Component(compactLayout: Boolean) { val context = LocalContext.current val title by musicService.title.collectAsState(null) @@ -58,7 +57,7 @@ class MusicPartProvider : PartProvider, KoinComponent { val playIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause) - if (layout === ClockWidgetLayout.Horizontal) { + if (compactLayout) { Row( verticalAlignment = Alignment.CenterVertically ) { @@ -113,7 +112,7 @@ class MusicPartProvider : PartProvider, KoinComponent { } } } - if (layout === ClockWidgetLayout.Vertical) { + if (!compactLayout) { Column( modifier = Modifier.padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/PartProvider.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/PartProvider.kt index f2103a55..adf3a836 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/PartProvider.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/clock/parts/PartProvider.kt @@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts import android.content.Context import androidx.compose.runtime.Composable -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout import kotlinx.coroutines.flow.Flow interface PartProvider { @@ -12,5 +11,5 @@ interface PartProvider { fun setTime(time: Long) {} @Composable - fun Component(layout: ClockWidgetLayout) + fun Component(compactLayout: Boolean) } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt index 6cea8347..1f8c27a9 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidget.kt @@ -7,6 +7,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Tag import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -30,6 +31,10 @@ fun FavoritesWidget(widget: FavoritesWidget) { val tagsExpanded by viewModel.tagsExpanded.collectAsState(false) + LaunchedEffect(widget) { + viewModel.updateWidget(widget) + } + Column { if (favorites.isNotEmpty()) { SearchResultGrid(favorites) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt index 7db015d3..9ac7a142 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt @@ -1,32 +1,34 @@ package de.mm20.launcher2.ui.launcher.widgets.favorites +import de.mm20.launcher2.preferences.ui.GridSettings +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.services.widgets.WidgetsService -import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout import de.mm20.launcher2.ui.common.FavoritesVM -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted +import de.mm20.launcher2.widgets.FavoritesWidget +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.launch import org.koin.core.component.inject class FavoritesWidgetVM : FavoritesVM() { private val widgetsService: WidgetsService by inject() - override val tagsExpanded: Flow = dataStore.data.map { it.ui.widgetTagsMultiline } - .shareIn(viewModelScope, SharingStarted.Lazily) + private val uiSettings: UiSettings by inject() + + override val tagsExpanded: MutableStateFlow = MutableStateFlow(false) + private val widget = MutableStateFlow(null) private val isTopWidget = widgetsService.isFavoritesWidgetFirst() - private val clockWidgetFavSlots = dataStore.data.combine(isTopWidget) { data, isTop -> - if (!isTop || !data.clockWidget.favoritesPart) 0 - else { - if (data.clockWidget.layout == ClockWidgetLayout.Horizontal) data.grid.columnCount - 2 - else data.grid.columnCount + private val clockWidgetFavSlots = + combine(uiSettings.dock, isTopWidget, uiSettings.gridSettings) { (dock, isTop, grid) -> + dock as Boolean + isTop as Boolean + grid as GridSettings + if (!isTop || !dock) 0 + else { + grid.columnCount + } } - } override val favorites = super.favorites.combine(clockWidgetFavSlots) { favs, slots -> if (selectedTag.value == null) { @@ -38,17 +40,16 @@ class FavoritesWidgetVM : FavoritesVM() { } override fun setTagsExpanded(expanded: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setUi( - it.ui.toBuilder() - .setWidgetTagsMultiline(expanded) - .build() - ) - .build() - } - } + val widget = this.widget.value ?: return + widgetsService.updateWidget( + widget.copy( + config = widget.config.copy(tagsMultiline = expanded) + ) + ) + } + + fun updateWidget(widget: FavoritesWidget) { + tagsExpanded.value = widget.config.tagsMultiline } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidgetVM.kt index a865322f..ca851c65 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidgetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidgetVM.kt @@ -5,11 +5,10 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.* import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.weather.DailyForecast import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.WeatherRepository -import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map @@ -26,8 +25,6 @@ class WeatherWidgetVM : ViewModel(), KoinComponent { private val permissionsManager: PermissionsManager by inject() - private val dataStore: LauncherDataStore by inject() - /** * Index of the currently selected day in [dailyForecasts] */ @@ -111,7 +108,8 @@ class WeatherWidgetVM : ViewModel(), KoinComponent { val autoLocation = weatherSettings.autoLocation .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val imperialUnits = dataStore.data.map { it.weather.imperialUnits } + val imperialUnits = weatherSettings.imperialUnits + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) fun selectDay(index: Int) { selectedDayIndex = min(index, forecasts.lastIndex) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/locals/CompositionLocals.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/locals/CompositionLocals.kt index 14c5311c..77caaafa 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/locals/CompositionLocals.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/locals/CompositionLocals.kt @@ -5,8 +5,8 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.geometry.Size import androidx.navigation.NavController -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.GridSettings +import de.mm20.launcher2.preferences.ui.CardStyle +import de.mm20.launcher2.preferences.ui.GridSettings import de.mm20.launcher2.ui.theme.WallpaperColors val LocalWindowSize = compositionLocalOf { Size(0f, 0f) } @@ -15,11 +15,11 @@ val LocalAppWidgetHost = compositionLocalOf(defaultFactory = { n val LocalNavController = compositionLocalOf { null } -val LocalCardStyle = compositionLocalOf { Settings.CardSettings.getDefaultInstance() } +val LocalCardStyle = compositionLocalOf { CardStyle() } val LocalFavoritesEnabled = compositionLocalOf { true } -val LocalGridSettings = compositionLocalOf { GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build() } +val LocalGridSettings = compositionLocalOf { GridSettings() } val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt index 08e4e683..587f5654 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt @@ -34,7 +34,6 @@ import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen -import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen @@ -170,9 +169,6 @@ class SettingsActivity : BaseActivity() { composable(ROUTE_MEDIA_INTEGRATION) { MediaIntegrationSettingsScreen() } - composable("settings/homescreen/clock") { - ClockWidgetSettingsScreen() - } composable("settings/favorites") { FavoritesSettingsScreen() } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index 20700f08..8f51ebb4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -9,9 +9,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings.AppearanceSettings -import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme -import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.ColorScheme +import de.mm20.launcher2.preferences.Font import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.preferences.ListPreference import de.mm20.launcher2.ui.component.preferences.Preference @@ -26,22 +25,22 @@ fun AppearanceSettingsScreen() { val viewModel: AppearanceSettingsScreenVM = viewModel() val context = LocalContext.current val navController = LocalNavController.current - val themeName by viewModel.colorSchemeName.collectAsStateWithLifecycle(null) + val themeName by viewModel.themeName.collectAsStateWithLifecycle(null) PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) { item { PreferenceCategory { - val theme by viewModel.theme.collectAsState() + val theme by viewModel.colorScheme.collectAsState() ListPreference( title = stringResource(id = R.string.preference_theme), items = listOf( - stringResource(id = R.string.preference_theme_system) to Theme.System, - stringResource(id = R.string.preference_theme_light) to Theme.Light, - stringResource(id = R.string.preference_theme_dark) to Theme.Dark, + stringResource(id = R.string.preference_theme_system) to ColorScheme.System, + stringResource(id = R.string.preference_theme_light) to ColorScheme.Light, + stringResource(id = R.string.preference_theme_dark) to ColorScheme.Dark, ), value = theme, onValueChanged = { newValue -> if (newValue == null) return@ListPreference - viewModel.setTheme(newValue) + viewModel.setColorScheme(newValue) } ) Preference( @@ -55,8 +54,8 @@ fun AppearanceSettingsScreen() { ListPreference( title = stringResource(R.string.preference_font), items = listOf( - "Outfit" to AppearanceSettings.Font.Outfit, - stringResource(R.string.preference_font_system) to AppearanceSettings.Font.SystemDefault, + "Outfit" to Font.Outfit, + stringResource(R.string.preference_font_system) to Font.System, ), value = font, onValueChanged = { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index c300bb35..69798023 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -3,44 +3,30 @@ package de.mm20.launcher2.ui.settings.appearance import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.icons.IconService -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font -import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme +import de.mm20.launcher2.preferences.ColorScheme +import de.mm20.launcher2.preferences.Font +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.ThemeRepository import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import java.util.UUID class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() - private val iconService: IconService by inject() private val themeRepository: ThemeRepository by inject() - val theme = dataStore.data.map { it.appearance.theme } + val colorScheme = uiSettings.colorScheme .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setTheme(theme: Theme) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance(it.appearance.toBuilder().setTheme(theme)) - .build() - } - } + fun setColorScheme(colorScheme: ColorScheme) { + uiSettings.setColorScheme(colorScheme) } - val colorSchemeName = dataStore.data.map { - it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let { - UUID.fromString(it) - } - } - .flatMapLatest { + val themeName = uiSettings.theme.flatMapLatest { themeRepository.getThemeOrDefault(it) }.map { it.name @@ -48,16 +34,10 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val font = dataStore.data.map { it.appearance.font } + val font = uiSettings.font .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setFont(font: Font) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance(it.appearance.toBuilder().setFont(font)) - .build() - } - } + uiSettings.setFont(font) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/buildinfo/BuildInfoSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/buildinfo/BuildInfoSettingsScreenVM.kt index 90bb8e23..6f1a4c4a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/buildinfo/BuildInfoSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/buildinfo/BuildInfoSettingsScreenVM.kt @@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings.buildinfo import androidx.lifecycle.ViewModel import de.mm20.launcher2.accounts.AccountType import de.mm20.launcher2.accounts.AccountsRepository -import de.mm20.launcher2.preferences.Settings.WeatherSettings.WeatherProvider import de.mm20.launcher2.weather.WeatherRepository import kotlinx.coroutines.flow.map import org.koin.core.component.KoinComponent diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt index 015656bf..d7be6d34 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt @@ -15,7 +15,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.SurfaceShape +import de.mm20.launcher2.preferences.ui.CardStyle import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.LauncherCard import de.mm20.launcher2.ui.component.preferences.ListPreference @@ -26,6 +28,9 @@ import de.mm20.launcher2.ui.component.preferences.SliderPreference @Composable fun CardsSettingsScreen() { val viewModel: CardsSettingsScreenVM = viewModel() + + val cardStyle by viewModel.cardStyle.collectAsState(CardStyle()) + PreferenceScreen(title = stringResource(R.string.preference_cards)) { item { Box( @@ -43,23 +48,21 @@ fun CardsSettingsScreen() { } item { PreferenceCategory { - val shape by viewModel.shape.collectAsState() ListPreference( icon = Icons.Rounded.Rectangle, title = stringResource(R.string.preference_cards_shape), items = listOf( - stringResource(R.string.preference_cards_shape_rounded) to Settings.CardSettings.Shape.Rounded, - stringResource(R.string.preference_cards_shape_cut) to Settings.CardSettings.Shape.Cut, + stringResource(R.string.preference_cards_shape_rounded) to SurfaceShape.Rounded, + stringResource(R.string.preference_cards_shape_cut) to SurfaceShape.Cut, ), - value = shape, + value = cardStyle.shape, onValueChanged = { - if (it != null) viewModel.setShape(it) + viewModel.setShape(it) }) - val radius by viewModel.radius.collectAsState() SliderPreference( title = stringResource(R.string.preference_cards_corner_radius), icon = Icons.Rounded.RoundedCorner, - value = radius, + value = cardStyle.cornerRadius, min = 0, max = 24, step = 1, @@ -67,22 +70,20 @@ fun CardsSettingsScreen() { viewModel.setRadius(it) } ) - val opacity by viewModel.opacity.collectAsState() SliderPreference( title = stringResource(R.string.preference_cards_opacity), icon = Icons.Rounded.Opacity, - value = opacity, + value = cardStyle.opacity, min = 0f, max = 1f, onValueChanged = { viewModel.setOpacity(it) } ) - val borderWidth by viewModel.borderWidth.collectAsState() SliderPreference( title = stringResource(R.string.preference_cards_stroke_width), icon = Icons.Rounded.LineWeight, - value = borderWidth, + value = cardStyle.borderWidth, min = 0, max = 8, step = 1, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt index 1d20cacf..d5721b59 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt @@ -1,70 +1,29 @@ package de.mm20.launcher2.ui.settings.cards import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch +import de.mm20.launcher2.preferences.SurfaceShape +import de.mm20.launcher2.preferences.ui.UiSettings import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class CardsSettingsScreenVM: ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() +class CardsSettingsScreenVM : ViewModel(), KoinComponent { + private val uiSettings: UiSettings by inject() + + val cardStyle = uiSettings.cardStyle - val opacity = dataStore.data.map { it.cards.opacity } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0f) fun setOpacity(opacity: Float) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setCards(it.cards.toBuilder() - .setOpacity(opacity) - ).build() - } - } + uiSettings.setCardOpacity(opacity) } - val radius = dataStore.data.map { it.cards.radius } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) - fun setRadius(radius: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setCards(it.cards.toBuilder() - .setRadius(radius) - ).build() - } - } + uiSettings.setCardRadius(radius) } - val borderWidth = dataStore.data.map { it.cards.borderWidth } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) fun setBorderWidth(borderWidth: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setCards(it.cards.toBuilder() - .setBorderWidth(borderWidth) - ).build() - } - } + uiSettings.setCardBorderWidth(borderWidth) } - val shape = dataStore.data.map { it.cards.shape } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - - fun setShape(shape: Settings.CardSettings.Shape) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setCards(it.cards.toBuilder() - .setShape(shape) - ).build() - } - } + fun setShape(shape: SurfaceShape) { + uiSettings.setCardShape(shape) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt deleted file mode 100644 index ea8238e2..00000000 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreen.kt +++ /dev/null @@ -1,204 +0,0 @@ -package de.mm20.launcher2.ui.settings.clockwidget - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.* -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import com.google.accompanist.pager.HorizontalPagerIndicator -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout -import de.mm20.launcher2.ui.launcher.widgets.clock.Clock -import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.component.preferences.* - -@Composable -fun ClockWidgetSettingsScreen() { - val viewModel: ClockWidgetSettingsScreenVM = viewModel() - PreferenceScreen( - title = stringResource(R.string.preference_screen_clockwidget), - helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/widgets/clock" - ) { - item { - PreferenceCategory { - val layout by viewModel.layout.collectAsState() - ListPreference( - title = stringResource(R.string.preference_clockwidget_layout), - value = layout, - items = listOf( - stringResource(R.string.preference_clockwidget_layout_vertical) to ClockWidgetLayout.Vertical, - stringResource(R.string.preference_clockwidget_layout_horizontal) to ClockWidgetLayout.Horizontal, - ), - onValueChanged = { - if (it != null) viewModel.setLayout(it) - } - ) - val clockStyle by viewModel.clockStyle.collectAsState() - ClockStylePreference( - layout = layout ?: ClockWidgetLayout.Vertical, - value = clockStyle, - onValueChanged = { - viewModel.setClockStyle(it) - } - ) - val color by viewModel.color.collectAsState() - ListPreference( - title = stringResource(R.string.preference_clock_widget_color), - value = color, - items = listOf( - stringResource(R.string.preference_system_bar_icons_auto) to ClockWidgetColors.Auto, - stringResource(R.string.preference_system_bar_icons_light) to ClockWidgetColors.Light, - stringResource(R.string.preference_system_bar_icons_dark) to ClockWidgetColors.Dark, - ), - onValueChanged = { - if (it != null) viewModel.setColor(it) - } - ) - val fillHeight by viewModel.fillHeight.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clock_widget_fill_height), - summary = stringResource(R.string.preference_clock_widget_fill_height_summary), - value = fillHeight == true, - onValueChanged = { viewModel.setFillHeight(it) } - ) - } - } - item { - PreferenceCategory { - val datePart by viewModel.datePart.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clockwidget_date_part), - summary = stringResource(R.string.preference_clockwidget_date_part_summary), - icon = Icons.Rounded.Today, - value = datePart == true, - onValueChanged = { - viewModel.setDatePart(it) - }, - ) - val favoritesPart by viewModel.favoritesPart.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clockwidget_favorites_part), - summary = stringResource(R.string.preference_clockwidget_favorites_part_summary), - icon = Icons.Rounded.Star, - value = favoritesPart == true, - onValueChanged = { - viewModel.setFavoritesPart(it) - }, - ) - } - } - item { - PreferenceCategory { - val musicPart by viewModel.musicPart.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clockwidget_music_part), - summary = stringResource(R.string.preference_clockwidget_music_part_summary), - icon = Icons.Rounded.MusicNote, - value = musicPart == true, - onValueChanged = { - viewModel.setMusicPart(it) - } - ) - val alarmPart by viewModel.alarmPart.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clockwidget_alarm_part), - summary = stringResource(R.string.preference_clockwidget_alarm_part_summary), - icon = Icons.Rounded.Alarm, - value = alarmPart == true, - onValueChanged = { - viewModel.setAlarmPart(it) - } - ) - val batteryPart by viewModel.batteryPart.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_clockwidget_battery_part), - summary = stringResource(R.string.preference_clockwidget_battery_part_summary), - icon = Icons.Rounded.BatteryFull, - value = batteryPart == true, - onValueChanged = { - viewModel.setBatteryPart(it) - } - ) - } - } - } -} - -@Composable -fun ClockStylePreference( - layout: ClockWidgetLayout, - value: ClockStyle?, - onValueChanged: (ClockStyle) -> Unit -) { - var showDialog by remember { mutableStateOf(false) } - Preference( - title = stringResource(R.string.preference_clock_widget_style), - summary = stringResource(R.string.preference_clock_widget_style_summary), - onClick = { - showDialog = true - } - ) - if (showDialog && value != null) { - val styles = remember { - ClockStyle.values().filter { it != ClockStyle.UNRECOGNIZED } - } - val pagerState = rememberPagerState(styles.indexOf(value)) { styles.size } - - AlertDialog( - onDismissRequest = { showDialog = false }, - confirmButton = { - TextButton(onClick = { - showDialog = false - onValueChanged(styles[pagerState.currentPage]) - }) { - Text( - text = stringResource(android.R.string.ok), - ) - } - }, - dismissButton = { - TextButton(onClick = { showDialog = false }) { - Text( - text = stringResource(android.R.string.cancel), - ) - } - }, - - text = { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - HorizontalPager( - state = pagerState, - modifier = Modifier.height(300.dp).fillMaxWidth() - ) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Clock( - style = styles[it], - layout = layout - ) - } - } - HorizontalPagerIndicator(pagerState = pagerState, pageCount = styles.size) - } - } - ) - } -} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt index 4e3b4697..5abc7f7f 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/clockwidget/ClockWidgetSettingsScreenVM.kt @@ -2,9 +2,10 @@ package de.mm20.launcher2.ui.settings.clockwidget import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings -import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetAlignment +import de.mm20.launcher2.preferences.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.ui.ClockWidgetSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -13,146 +14,62 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() - val layout = dataStore.data.map { it.clockWidget.layout } + private val settings: ClockWidgetSettings by inject() + val compact = settings.compact .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setLayout(layout: ClockWidgetSettings.ClockWidgetLayout) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setLayout(layout) - ).build() - } - } + fun setCompact(compact: Boolean) { + settings.setCompact(compact) } - val clockStyle = dataStore.data.map { it.clockWidget.clockStyle } + val clockStyle = settings.clockStyle .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setClockStyle(clockStyle: ClockWidgetSettings.ClockStyle) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setClockStyle(clockStyle) - ).build() - } - } + fun setClockStyle(clockStyle: ClockWidgetStyle) { + settings.setClockStyle(clockStyle) } - val color = dataStore.data.map { it.clockWidget.color } + val color = settings.color .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setColor(color: ClockWidgetColors) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setColor(color) - ).build() - } - } + settings.setColor(color) } - val fillHeight = dataStore.data.map { it.clockWidget.fillHeight } + val fillHeight = settings.fillHeight .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setFillHeight(fillHeight: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setFillHeight(fillHeight) - ).build() - } - } + settings.setFillHeight(fillHeight) } - val datePart = dataStore.data.map { it.clockWidget.datePart } + val dock = settings.dock .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + + val parts = settings.parts + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setDatePart(datePart: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setDatePart(datePart) - ).build() - } - } + settings.setDatePart(datePart) } - val favoritesPart = dataStore.data.map { it.clockWidget.favoritesPart } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setFavoritesPart(favoritesPart: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setFavoritesPart(favoritesPart) - ).build() - } - } + settings.setDock(favoritesPart) } - val batteryPart = dataStore.data.map { it.clockWidget.batteryPart } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setBatteryPart(batteryPart: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setBatteryPart(batteryPart) - ).build() - } - } + settings.setBatteryPart(batteryPart) } - val musicPart = dataStore.data.map { it.clockWidget.musicPart } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setMusicPart(musicPart: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setMusicPart(musicPart) - ).build() - } - } + settings.setMusicPart(musicPart) } - val alarmPart = dataStore.data.map { it.clockWidget.alarmPart } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setAlarmPart(alarmPart: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setAlarmPart(alarmPart) - ).build() - } - } + settings.setAlarmPart(alarmPart) } - val alignment = dataStore.data.map { it.clockWidget.alignment } + val alignment = settings.alignment .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setAlignment(alignment: ClockWidgetSettings.ClockWidgetAlignment) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setClockWidget( - it.clockWidget.toBuilder() - .setAlignment(alignment) - ).build() - } - } + fun setAlignment(alignment: ClockWidgetAlignment) { + settings.setAlignment(alignment) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt index 51ab7035..811ba185 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt @@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.colorscheme import android.content.Context import android.content.Intent -import android.net.Uri -import android.util.Log import androidx.core.content.FileProvider import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.protobuf.ByteString -import de.mm20.launcher2.ktx.toBytes import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ThemeDescriptor +import de.mm20.launcher2.preferences.ui.UiSettings +import de.mm20.launcher2.themes.BlackAndWhiteThemeId import de.mm20.launcher2.themes.DefaultThemeId import de.mm20.launcher2.themes.Theme import de.mm20.launcher2.themes.ThemeRepository -import de.mm20.launcher2.themes.fromJson import de.mm20.launcher2.themes.toJson import de.mm20.launcher2.ui.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -31,12 +27,14 @@ import java.util.UUID class ThemesSettingsScreenVM : ViewModel(), KoinComponent { private val themeRepository: ThemeRepository by inject() - private val dataStore: LauncherDataStore by inject() + private val uiSettings: UiSettings by inject() - val selectedTheme: Flow = dataStore.data.map { - it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let { - UUID.fromString(it) - } ?: DefaultThemeId + val selectedTheme = uiSettings.theme.map { + when(it) { + ThemeDescriptor.Default -> DefaultThemeId + ThemeDescriptor.BlackAndWhite -> BlackAndWhiteThemeId + is ThemeDescriptor.Custom -> UUID.fromString(it.id) + } } val themes: Flow> = themeRepository.getThemes() @@ -49,15 +47,10 @@ class ThemesSettingsScreenVM : ViewModel(), KoinComponent { } fun selectTheme(theme: Theme) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance( - it.appearance.toBuilder() - .setThemeId(theme.id.toString()) - ) - .build() - } + when(theme.id) { + DefaultThemeId -> ThemeDescriptor.Default + BlackAndWhiteThemeId -> ThemeDescriptor.BlackAndWhite + else -> ThemeDescriptor.Custom(theme.id.toString()) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/easteregg/EasterEggSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/easteregg/EasterEggSettingsScreenVM.kt index 2602208c..f55f487e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/easteregg/EasterEggSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/easteregg/EasterEggSettingsScreenVM.kt @@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.easteregg import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.ui.UiSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject class EasterEggSettingsScreenVM: ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val settings: UiSettings by inject() - val easterEgg = dataStore.data.map { it.easterEgg } + val easterEgg = settings.iconShape.map { it == IconShape.EasterEgg } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) fun setEasterEgg(easterEgg: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setEasterEgg(easterEgg).build() - } - } + settings.setIconShape(if (easterEgg) IconShape.EasterEgg else IconShape.PlatformDefault) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreen.kt index f9b20153..fa57ad25 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreen.kt @@ -14,7 +14,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor +import de.mm20.launcher2.preferences.WeightFactor import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.preferences.ListPreference import de.mm20.launcher2.ui.component.preferences.Preference diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreenVM.kt index dd7c0280..4012d075 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/favorites/FavoritesSettingsScreenVM.kt @@ -2,8 +2,9 @@ package de.mm20.launcher2.ui.settings.favorites import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor +import de.mm20.launcher2.preferences.WeightFactor +import de.mm20.launcher2.preferences.search.FavoritesSettings +import de.mm20.launcher2.preferences.search.RankingSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -12,65 +13,30 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject class FavoritesSettingsScreenVM: ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val favoritesSettings: FavoritesSettings by inject() + private val rankingSettings: RankingSettings by inject() - val frequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed } + val frequentlyUsed = favoritesSettings.frequentlyUsed .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setFrequentlyUsed(frequentlyUsed: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFavorites( - it.favorites.toBuilder() - .setFrequentlyUsed(frequentlyUsed) - ) - .build() - } - } + favoritesSettings.setFrequentlyUsed(frequentlyUsed) } - val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows } + val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1) fun setFrequentlyUsedRows(frequentlyUsedRows: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFavorites( - it.favorites.toBuilder() - .setFrequentlyUsedRows(frequentlyUsedRows) - ) - .build() - } - } + favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows) } - val editButton = dataStore.data.map { it.favorites.editButton } + val editButton = favoritesSettings.showEditButton .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setEditButton(editButton: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setFavorites( - it.favorites.toBuilder() - .setEditButton(editButton) - ) - .build() - } - } + favoritesSettings.setShowEditButton(editButton) } - val searchResultWeightFactor = dataStore.data.map { it.resultOrdering.weightFactor } + val searchResultWeightFactor = rankingSettings.weightFactor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), WeightFactor.Default) fun setSearchResultWeightFactor(searchResultWeightFactor: WeightFactor) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setResultOrdering( - it.resultOrdering.toBuilder() - .setWeightFactor(searchResultWeightFactor) - ) - .build() - } - } + rankingSettings.setWeightFactor(searchResultWeightFactor) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/filesearch/FileSearchSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/filesearch/FileSearchSettingsScreenVM.kt index be2eed2a..daa1259a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/filesearch/FileSearchSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/filesearch/FileSearchSettingsScreenVM.kt @@ -7,11 +7,11 @@ import androidx.lifecycle.viewModelScope import de.mm20.launcher2.accounts.Account import de.mm20.launcher2.accounts.AccountType import de.mm20.launcher2.accounts.AccountsRepository -import de.mm20.launcher2.files.settings.FileSearchSettings import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugins.PluginService +import de.mm20.launcher2.preferences.search.FileSearchSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt index b8e5d0a0..0c5152c0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt @@ -27,8 +27,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction -import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings.Layout import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.common.SearchablePicker @@ -47,14 +48,14 @@ fun GestureSettingsScreen() { val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null) val options = buildList { - add(stringResource(R.string.gesture_action_none) to GestureAction.None) - add(stringResource(R.string.gesture_action_notifications) to GestureAction.OpenNotificationDrawer) - add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.OpenQuickSettings) - if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.LockScreen) - add(stringResource(R.string.gesture_action_recents) to GestureAction.OpenRecents) - add(stringResource(R.string.gesture_action_power_menu) to GestureAction.OpenPowerDialog) - add(stringResource(R.string.gesture_action_open_search) to GestureAction.OpenSearch) - add(stringResource(R.string.gesture_action_launch_app) to GestureAction.LaunchApp) + add(stringResource(R.string.gesture_action_none) to GestureAction.NoAction) + add(stringResource(R.string.gesture_action_notifications) to GestureAction.Notifications) + add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.QuickSettings) + if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.ScreenLock) + add(stringResource(R.string.gesture_action_recents) to GestureAction.Recents) + add(stringResource(R.string.gesture_action_power_menu) to GestureAction.PowerMenu) + add(stringResource(R.string.gesture_action_open_search) to GestureAction.Search) + add(stringResource(R.string.gesture_action_launch_app) to GestureAction.Launch(null)) } val context = LocalContext.current @@ -64,9 +65,9 @@ fun GestureSettingsScreen() { val baseLayout by viewModel.baseLayout.collectAsStateWithLifecycle(null) ListPreference(title = stringResource(R.string.preference_layout_open_search), items = listOf( - stringResource(R.string.open_search_pull_down) to Layout.PullDown, - stringResource(R.string.open_search_swipe_left) to Layout.Pager, - stringResource(R.string.open_search_swipe_right) to Layout.PagerReversed, + stringResource(R.string.open_search_pull_down) to BaseLayout.PullDown, + stringResource(R.string.open_search_swipe_left) to BaseLayout.Pager, + stringResource(R.string.open_search_swipe_right) to BaseLayout.PagerReversed, ), value = baseLayout, onValueChanged = { @@ -125,7 +126,7 @@ fun GestureSettingsScreen() { ) val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null) - val swipeDownIsSearch = layout == Layout.PullDown + val swipeDownIsSearch = layout == BaseLayout.PullDown AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown) && !swipeDownIsSearch) { MissingPermissionBanner( modifier = Modifier.padding(16.dp), @@ -149,7 +150,7 @@ fun GestureSettingsScreen() { ) val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null) - val swipeLeftIsSearch = layout == Layout.Pager + val swipeLeftIsSearch = layout == BaseLayout.Pager AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft) && !swipeLeftIsSearch) { MissingPermissionBanner( modifier = Modifier.padding(16.dp), @@ -173,7 +174,7 @@ fun GestureSettingsScreen() { ) val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null) - val swipeRightIsSearch = layout == Layout.PagerReversed + val swipeRightIsSearch = layout == BaseLayout.PagerReversed AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight) && !swipeRightIsSearch) { MissingPermissionBanner( modifier = Modifier.padding(16.dp), @@ -225,11 +226,11 @@ fun GestureSettingsScreen() { fun requiresAccessibilityService(action: GestureAction?): Boolean { return when (action) { - GestureAction.OpenNotificationDrawer, - GestureAction.LockScreen, - GestureAction.OpenQuickSettings, - GestureAction.OpenRecents, - GestureAction.OpenPowerDialog -> true + is GestureAction.Notifications, + is GestureAction.ScreenLock, + is GestureAction.QuickSettings, + is GestureAction.Recents, + is GestureAction.PowerMenu -> true else -> false } } @@ -256,12 +257,12 @@ fun GesturePreference( title = title, enabled = !isOpenSearch, items = options, - value = if (isOpenSearch) GestureAction.OpenSearch else value, + value = if (isOpenSearch) GestureAction.Search else value, onValueChanged = { if (it != null) onValueChanged(it) } ) } - if (value == GestureAction.LaunchApp && !isOpenSearch) { + if (value is GestureAction.Launch && !isOpenSearch) { Box( modifier = Modifier .height(36.dp) @@ -277,12 +278,12 @@ fun GesturePreference( } } - if (!isOpenSearch && value == GestureAction.LaunchApp && (showAppPicker || app == null)) { + if (!isOpenSearch && value is GestureAction.Launch && (showAppPicker || app == null)) { SearchablePicker( title = { Text(title) }, onDismissRequest = { showAppPicker = false - if (app == null) onValueChanged(GestureAction.None) + if (app == null) onValueChanged(GestureAction.NoAction) }, value = app, onValueChanged = { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt index fcd4c742..1c1f775f 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreenVM.kt @@ -3,15 +3,16 @@ package de.mm20.launcher2.ui.settings.gestures import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.ui.GestureSettings +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.search.SavableSearchable +import de.mm20.launcher2.searchable.SavableSearchableRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.emptyFlow @@ -22,7 +23,8 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject class GestureSettingsScreenVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val gestureSettings: GestureSettings by inject() + private val uiSettings: UiSettings by inject() private val permissionsManager: PermissionsManager by inject() private val searchableRepository: SavableSearchableRepository by inject() private val iconService: IconService by inject() @@ -30,208 +32,124 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent { val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val baseLayout = dataStore.data.map { it.layout.baseLayout } + val baseLayout = uiSettings.baseLayout .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setBaseLayout(baseLayout: Settings.LayoutSettings.Layout) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setLayout(it.layout.toBuilder().setBaseLayout(baseLayout)) - .build() - } - } + fun setBaseLayout(baseLayout: BaseLayout) { + uiSettings.setBaseLayout(baseLayout) } - val swipeDown = dataStore.data.map { it.gestures.swipeDown } + + val swipeDown = gestureSettings.swipeDown .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val swipeLeft = dataStore.data.map { it.gestures.swipeLeft } + val swipeLeft = gestureSettings.swipeLeft .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val swipeRight = dataStore.data.map { it.gestures.swipeRight } + val swipeRight = gestureSettings.swipeRight .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val doubleTap = dataStore.data.map { it.gestures.doubleTap } + val doubleTap = gestureSettings.doubleTap .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val longPress = dataStore.data.map { it.gestures.longPress } + val longPress = gestureSettings.longPress .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val homeButton = dataStore.data.map { it.gestures.homeButton } + val homeButton = gestureSettings.homeButton .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setSwipeDown(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeDown(action).build()) - .build() - } - } + gestureSettings.setSwipeDown(action) } fun setSwipeLeft(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeLeft(action).build()) - .build() - } - } + gestureSettings.setSwipeLeft(action) } fun setSwipeRight(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeRight(action).build()) - .build() - } - } + gestureSettings.setSwipeRight(action) } fun setDoubleTap(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setDoubleTap(action).build()) - .build() - } - } + gestureSettings.setDoubleTap(action) } fun setLongPress(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setLongPress(action).build()) - .build() - } - } + gestureSettings.setLongPress(action) } fun setHomeButton(action: GestureAction) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setGestures(it.gestures.toBuilder().setHomeButton(action).build()) - .build() - } - } + gestureSettings.setHomeButton(action) } - val swipeLeftApp: Flow = dataStore.data.map { it.gestures.swipeLeftApp } + val swipeLeftApp: Flow = swipeLeft .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setSwipeLeftApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setSwipeLeftApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setSwipeLeft(GestureAction.Launch(searchable.key)) } - val swipeRightApp: Flow = dataStore.data.map { it.gestures.swipeRightApp } + val swipeRightApp: Flow = swipeRight .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setSwipeRightApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setSwipeRightApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setSwipeRight(GestureAction.Launch(searchable.key)) } - val swipeDownApp: Flow = dataStore.data.map { it.gestures.swipeDownApp } + val swipeDownApp: Flow = swipeDown .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setSwipeDownApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setSwipeDownApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setSwipeDown(GestureAction.Launch(searchable.key)) } - val longPressApp: Flow = dataStore.data.map { it.gestures.longPressApp } + val longPressApp: Flow = longPress .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setLongPressApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setLongPressApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setLongPress(GestureAction.Launch(searchable.key)) } - val doubleTapApp: Flow = dataStore.data.map { it.gestures.doubleTapApp } + val doubleTapApp: Flow = doubleTap .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setDoubleTapApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setDoubleTapApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setDoubleTap(GestureAction.Launch(searchable.key)) } - val homeButtonApp: Flow = dataStore.data.map { it.gestures.homeButtonApp } + val homeButtonApp: Flow = homeButton .map { - if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull() + if (it !is GestureAction.Launch || it.key == null) null + else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null) fun setHomeButtonApp(searchable: SavableSearchable?) { - viewModelScope.launch { - searchable?.let { searchableRepository.insert(it) } - dataStore.updateData { - it.toBuilder() - .setGestures(it.gestures.toBuilder() - .setHomeButtonApp(searchable?.key ?: "") - .build() - ) - .build() - } - } + searchable?.let { searchableRepository.insert(it) } ?: return + setHomeButton(GestureAction.Launch(searchable.key)) } - fun requestPermission(context: AppCompatActivity) { permissionsManager.requestPermission(context, PermissionGroup.Accessibility) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/hiddenitems/HiddenItemsSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/hiddenitems/HiddenItemsSettingsScreenVM.kt index 1ea120d6..1642ab42 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/hiddenitems/HiddenItemsSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/hiddenitems/HiddenItemsSettingsScreenVM.kt @@ -9,7 +9,7 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ui.SearchUiSettings import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.Application import kotlinx.coroutines.Dispatchers @@ -29,7 +29,7 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent { private val appRepository: AppRepository by inject() private val searchableRepository: SavableSearchableRepository by inject() private val iconService: IconService by inject() - private val dataStore: LauncherDataStore by inject() + private val searchUiSettings: SearchUiSettings by inject() val allApps = appRepository.findMany().map { withContext(Dispatchers.Default) { it.sorted() } @@ -68,16 +68,10 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent { app.openAppDetails(context) } - val hiddenItemsButton = dataStore.data.map { it.searchBar.hiddenItemsButton } + val hiddenItemsButton = searchUiSettings.hiddenItemsButton .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) fun setHiddenItemsButton(hidden: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSearchBar(it.searchBar.toBuilder().setHiddenItemsButton(hidden)).build() - - } - } + searchUiSettings.setHiddenItemsButton(hidden) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreen.kt index db8dbdf1..669a7856 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreen.kt @@ -26,7 +26,10 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.pager.HorizontalPagerIndicator -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarStyle +import de.mm20.launcher2.preferences.SystemBarColors import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.SearchBar import de.mm20.launcher2.ui.component.SearchBarLevel @@ -109,14 +112,14 @@ fun HomescreenSettingsScreen() { viewModel.setSearchBarStyle(it) } ) - AnimatedVisibility(searchBarStyle == Settings.SearchBarSettings.SearchBarStyle.Transparent) { + AnimatedVisibility(searchBarStyle == SearchBarStyle.Transparent) { ListPreference( title = stringResource(R.string.preference_search_bar_color), value = searchBarColor, items = listOf( - stringResource(R.string.preference_system_bar_icons_auto) to Settings.SearchBarSettings.SearchBarColors.Auto, - stringResource(R.string.preference_system_bar_icons_light) to Settings.SearchBarSettings.SearchBarColors.Light, - stringResource(R.string.preference_system_bar_icons_dark) to Settings.SearchBarSettings.SearchBarColors.Dark, + stringResource(R.string.preference_system_bar_icons_auto) to SearchBarColors.Auto, + stringResource(R.string.preference_system_bar_icons_light) to SearchBarColors.Light, + stringResource(R.string.preference_system_bar_icons_dark) to SearchBarColors.Dark, ), onValueChanged = { if (it != null) viewModel.setSearchBarColor(it) @@ -208,9 +211,9 @@ fun HomescreenSettingsScreen() { title = stringResource(R.string.preference_status_bar_icons), value = lightStatusBar, items = listOf( - stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto, - stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light, - stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark, + stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto, + stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light, + stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark, ), onValueChanged = { if (it != null) viewModel.setLightStatusBar(it) @@ -220,9 +223,9 @@ fun HomescreenSettingsScreen() { title = stringResource(R.string.preference_nav_bar_icons), value = lightNavBar, items = listOf( - stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto, - stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light, - stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark, + stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto, + stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light, + stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark, ), onValueChanged = { if (it != null) viewModel.setLightNavBar(it) @@ -255,15 +258,14 @@ fun HomescreenSettingsScreen() { fun SearchBarStylePreference( title: String, summary: String? = null, - value: Settings.SearchBarSettings.SearchBarStyle?, - onValueChanged: (Settings.SearchBarSettings.SearchBarStyle) -> Unit + value: SearchBarStyle?, + onValueChanged: (SearchBarStyle) -> Unit ) { var showDialog by remember { mutableStateOf(false) } Preference(title = title, summary = summary, onClick = { showDialog = true }) if (showDialog && value != null) { val styles = remember { - Settings.SearchBarSettings.SearchBarStyle.values() - .filter { it != Settings.SearchBarSettings.SearchBarStyle.UNRECOGNIZED } + SearchBarStyle.entries } val pagerState = rememberPagerState(initialPage = styles.indexOf(value)) { styles.size } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreenVM.kt index ea199b21..6c14b276 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/homescreen/HomescreenSettingsScreenVM.kt @@ -13,8 +13,12 @@ import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.ScreenOrientation +import de.mm20.launcher2.preferences.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarStyle +import de.mm20.launcher2.preferences.SystemBarColors +import de.mm20.launcher2.preferences.ui.UiSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -23,52 +27,31 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.get class HomescreenSettingsScreenVM( - private val dataStore: LauncherDataStore, + private val uiSettings: UiSettings, ) : ViewModel() { var showClockWidgetSheet by mutableStateOf(false) - val dimWallpaper = dataStore.data.map { it.appearance.dimWallpaper } + val dimWallpaper = uiSettings.dimWallpaper .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) + fun setDimWallpaper(dimWallpaper: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance( - it.appearance.toBuilder() - .setDimWallpaper(dimWallpaper) - ).build() - } - } + uiSettings.setDimWallpaper(dimWallpaper) } - val blurWallpaper = dataStore.data.map { it.appearance.blurWallpaper } + val blurWallpaper = uiSettings.blurWallpaper .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) + fun setBlurWallpaper(blurWallpaper: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance( - it.appearance.toBuilder() - .setBlurWallpaper(blurWallpaper) - ).build() - } - } + uiSettings.setBlurWallpaper(blurWallpaper) } - val blurWallpaperRadius = dataStore.data.map { it.appearance.blurWallpaperRadius } + val blurWallpaperRadius = uiSettings.wallpaperBlurRadius .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32) + fun setBlurWallpaperRadius(blurWallpaperRadius: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAppearance( - it.appearance.toBuilder() - .setBlurWallpaperRadius(blurWallpaperRadius) - ).build() - } - } + uiSettings.setWallpaperBlurRadius(blurWallpaperRadius) } fun openWallpaperChooser(context: AppCompatActivity) { @@ -80,168 +63,88 @@ class HomescreenSettingsScreenVM( return context.getSystemService()?.isCrossWindowBlurEnabled == true } - val statusBarIcons = dataStore.data.map { it.systemBars.statusBarColor } + val statusBarIcons = uiSettings.statusBarColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setLightStatusBar(statusBarColor: Settings.SystemBarsSettings.SystemBarColors) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSystemBars( - it.systemBars.toBuilder() - .setStatusBarColor(statusBarColor) - ) - .build() - } - } + + fun setLightStatusBar(statusBarColor: SystemBarColors) { + uiSettings.setStatusBarColor(statusBarColor) } - val navBarIcons = dataStore.data.map { it.systemBars.navBarColor } + val navBarIcons = uiSettings.navigationBarColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setLightNavBar(navBarColors: Settings.SystemBarsSettings.SystemBarColors) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSystemBars( - it.systemBars.toBuilder() - .setNavBarColor(navBarColors) - ) - .build() - } - } + + fun setLightNavBar(navBarColors: SystemBarColors) { + uiSettings.setNavigationBarColor(navBarColors) } - val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar } + val hideStatusBar = uiSettings.hideStatusBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setHideStatusBar(hideStatusBar: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSystemBars( - it.systemBars.toBuilder() - .setHideStatusBar(hideStatusBar) - ) - .build() - } - } + uiSettings.setHideStatusBar(hideStatusBar) } - val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar } + val hideNavBar = uiSettings.hideNavigationBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setHideNavBar(hideNavBar: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSystemBars( - it.systemBars.toBuilder() - .setHideNavBar(hideNavBar) - ) - .build() - } - } + uiSettings.setHideNavigationBar(hideNavBar) } - val searchBarColor = dataStore.data.map { it.searchBar.color } + val searchBarColor = uiSettings.searchBarColor .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setSearchBarColor(color: Settings.SearchBarSettings.SearchBarColors) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSearchBar( - it.searchBar.toBuilder() - .setColor(color) - ) - .build() - } - } + + fun setSearchBarColor(color: SearchBarColors) { + uiSettings.setSearchBarColor(color) } - val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle } + val searchBarStyle = uiSettings.searchBarStyle .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setSearchBarStyle(searchBarStyle: Settings.SearchBarSettings.SearchBarStyle) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setSearchBar( - it.searchBar.toBuilder() - .setSearchBarStyle(searchBarStyle) - ) - .build() - } - } + + fun setSearchBarStyle(searchBarStyle: SearchBarStyle) { + uiSettings.setSearchBarStyle(searchBarStyle) } - val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar } + val fixedSearchBar = uiSettings.fixedSearchBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setFixedSearchBar(fixedSearchBar: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setLayout(it.layout.toBuilder().setFixedSearchBar(fixedSearchBar)) - .build() - } - } + uiSettings.setFixedSearchBar(fixedSearchBar) } - val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar } + val bottomSearchBar = uiSettings.bottomSearchBar .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setBottomSearchBar(bottomSearchBar: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setLayout(it.layout.toBuilder().setBottomSearchBar(bottomSearchBar)) - .build() - } - } + uiSettings.setBottomSearchBar(bottomSearchBar) } - val fixedRotation = dataStore.data.map { it.layout.fixedRotation } + val fixedRotation = uiSettings.orientation.map { it != ScreenOrientation.Auto } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setFixedRotation(fixedRotation: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setLayout(it.layout.toBuilder().setFixedRotation(fixedRotation)) - .build() - } - } + uiSettings.setOrientation(if (fixedRotation) ScreenOrientation.Portrait else ScreenOrientation.Auto) } - val widgetEditButton = dataStore.data.map { it.widgets.editButton } + val widgetEditButton = uiSettings.widgetEditButton .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setWidgetEditButton(editButton: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setWidgets( - it.widgets.toBuilder() - .setEditButton(editButton) - ) - .build() - } - } + uiSettings.setWidgetEditButton(editButton) } - val chargingAnimation = dataStore.data.map { it.animations.charging } + val chargingAnimation = uiSettings.chargingAnimation .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setChargingAnimation(chargingAnimation: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setAnimations( - it.animations.toBuilder() - .setCharging(chargingAnimation) - ) - .build() - } - } + uiSettings.setChargingAnimation(chargingAnimation) } companion object : KoinComponent { val Factory = viewModelFactory { initializer { HomescreenSettingsScreenVM( - dataStore = get() + uiSettings = get() ) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt index 93d8c57b..8d0c9d85 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreen.kt @@ -24,7 +24,6 @@ import androidx.compose.material3.FilledIconToggleButton import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedCard import androidx.compose.material3.PlainTooltipBox import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -50,7 +49,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.icons.StaticIconLayer import de.mm20.launcher2.icons.StaticLauncherIcon -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.ui.GridSettings +import de.mm20.launcher2.preferences.ui.IconSettings +import de.mm20.launcher2.preferences.ui.IconSettingsData import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.ShapedLauncherIcon @@ -63,25 +65,18 @@ import de.mm20.launcher2.ui.component.preferences.SliderPreference import de.mm20.launcher2.ui.component.preferences.SwitchPreference import de.mm20.launcher2.ui.component.preferences.label import de.mm20.launcher2.ui.component.preferences.value -import de.mm20.launcher2.ui.ktx.toPixels @Composable fun IconsSettingsScreen() { val viewModel: IconsSettingsScreenVM = viewModel(factory = IconsSettingsScreenVM.Factory) val context = LocalContext.current - val iconSize by viewModel.iconSize.collectAsStateWithLifecycle(48) + val grid by viewModel.grid.collectAsStateWithLifecycle(GridSettings()) + val icons by viewModel.icons.collectAsStateWithLifecycle(null) val density = LocalDensity.current - val showLabels by viewModel.showLabels.collectAsStateWithLifecycle(null) - val columnCount by viewModel.columnCount.collectAsStateWithLifecycle(5) - val iconShape by viewModel.iconShape.collectAsStateWithLifecycle(Settings.IconSettings.IconShape.PlatformDefault) - val adaptifyLegacyIcons by viewModel.adaptifyLegacyIcons.collectAsStateWithLifecycle(null) - val themedIcons by viewModel.themedIcons.collectAsStateWithLifecycle(null) + val iconShape by viewModel.iconShape.collectAsStateWithLifecycle(IconShape.PlatformDefault) - val iconPackPackage by viewModel.iconPack.collectAsStateWithLifecycle(null) - val iconPackThemed by viewModel.iconPackThemed.collectAsState(true) val installedIconPacks by viewModel.installedIconPacks.collectAsState(emptyList()) - val forceThemedIcons by viewModel.forceThemedIcons.collectAsStateWithLifecycle(null) val hasNotificationsPermission by viewModel.hasNotificationsPermission.collectAsStateWithLifecycle(null) @@ -91,8 +86,8 @@ fun IconsSettingsScreen() { val shortcutBadges by viewModel.shortcutBadges.collectAsStateWithLifecycle(null) val pluginBadges by viewModel.pluginBadges.collectAsStateWithLifecycle(null) - val previewIcons by remember(iconSize) { - viewModel.getPreviewIcons(with(density) { iconSize.dp.toPx() }.toInt()) + val previewIcons by remember(grid?.iconSize) { + viewModel.getPreviewIcons(with(density) { grid.iconSize.dp.toPx() }.toInt()) }.collectAsState( emptyList() ) @@ -102,7 +97,7 @@ fun IconsSettingsScreen() { PreferenceCategory(title = stringResource(R.string.preference_category_grid)) { SliderPreference( title = stringResource(R.string.preference_grid_icon_size), - value = iconSize, + value = grid.iconSize, step = 8, min = 32, max = 64, @@ -113,14 +108,14 @@ fun IconsSettingsScreen() { SwitchPreference( title = stringResource(R.string.preference_grid_labels), summary = stringResource(R.string.preference_grid_labels_summary), - value = showLabels == true, + value = grid.showLabels, onValueChanged = { viewModel.setShowLabels(it) } ) SliderPreference( title = stringResource(R.string.preference_grid_column_count), - value = columnCount, + value = grid.columnCount, min = 3, max = 12, onValueChanged = { @@ -148,7 +143,7 @@ fun IconsSettingsScreen() { modifier = Modifier.weight(1f), contentAlignment = Alignment.Center ) { - ShapedLauncherIcon(size = iconSize.dp, icon = { icon }) + ShapedLauncherIcon(size = grid.iconSize.dp, icon = { icon }) } } } @@ -165,7 +160,7 @@ fun IconsSettingsScreen() { SwitchPreference( title = stringResource(R.string.preference_enforce_icon_shape), summary = stringResource(R.string.preference_enforce_icon_shape_summary), - value = adaptifyLegacyIcons == true, + value = icons?.adaptify == true, onValueChanged = { viewModel.setAdaptifyLegacyIcons(it) } @@ -173,7 +168,7 @@ fun IconsSettingsScreen() { SwitchPreference( title = stringResource(R.string.preference_themed_icons), summary = stringResource(R.string.preference_themed_icons_summary), - value = themedIcons == true, + value = icons?.themedIcons == true, onValueChanged = { viewModel.setThemedIcons(it) } @@ -181,14 +176,14 @@ fun IconsSettingsScreen() { SwitchPreference( title = stringResource(R.string.preference_force_themed_icons), summary = stringResource(R.string.preference_force_themed_icons_summary), - value = forceThemedIcons == true, - enabled = themedIcons == true, + value = icons?.forceThemed == true, + enabled = icons?.themedIcons == true, onValueChanged = { viewModel.setForceThemedIcons(it) } ) val iconPack by remember { - derivedStateOf { installedIconPacks.firstOrNull { it.packageName == iconPackPackage } } + derivedStateOf { installedIconPacks.firstOrNull { it.packageName == icons?.iconPack } } } val items = installedIconPacks.map { it.name to it @@ -265,7 +260,7 @@ fun IconsSettingsScreen() { PlainTooltipBox(tooltip = { Text(stringResource(R.string.icon_pack_dynamic_colors)) }) { FilledIconToggleButton( modifier = Modifier.tooltipTrigger(), - checked = iconPackThemed, + checked = icons?.iconPackThemed == true, onCheckedChange = { viewModel.setIconPackThemed(it) }) { @@ -347,16 +342,16 @@ fun IconsSettingsScreen() { fun IconShapePreference( title: String, summary: String? = null, - value: Settings.IconSettings.IconShape?, - onValueChanged: (Settings.IconSettings.IconShape) -> Unit + value: IconShape?, + onValueChanged: (IconShape) -> Unit ) { var showDialog by remember { mutableStateOf(false) } Preference(title = title, summary = summary, onClick = { showDialog = true }) if (showDialog && value != null) { val shapes = remember { - Settings.IconSettings.IconShape.values() - .filter { it != Settings.IconSettings.IconShape.UNRECOGNIZED && it != Settings.IconSettings.IconShape.EasterEgg } + IconShape.entries + .filter { it != IconShape.EasterEgg } } Dialog(onDismissRequest = { showDialog = false }) { Surface( @@ -431,19 +426,19 @@ fun IconShapePreference( @Composable -private fun getShapeName(shape: Settings.IconSettings.IconShape?): String? { +private fun getShapeName(shape: IconShape?): String? { return stringResource( when (shape) { - Settings.IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle - Settings.IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon - Settings.IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square - Settings.IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle - Settings.IconSettings.IconShape.Square -> R.string.preference_icon_shape_square - Settings.IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon - Settings.IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform - Settings.IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle - Settings.IconSettings.IconShape.Teardrop -> R.string.preference_icon_shape_teardrop - Settings.IconSettings.IconShape.Pebble -> R.string.preference_icon_shape_pebble + IconShape.Triangle -> R.string.preference_icon_shape_triangle + IconShape.Hexagon -> R.string.preference_icon_shape_hexagon + IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square + IconShape.Squircle -> R.string.preference_icon_shape_squircle + IconShape.Square -> R.string.preference_icon_shape_square + IconShape.Pentagon -> R.string.preference_icon_shape_pentagon + IconShape.PlatformDefault -> R.string.preference_icon_shape_platform + IconShape.Circle -> R.string.preference_icon_shape_circle + IconShape.Teardrop -> R.string.preference_icon_shape_teardrop + IconShape.Pebble -> R.string.preference_icon_shape_pebble else -> return null } ) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreenVM.kt index 1370ae54..e367df1e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/icons/IconsSettingsScreenVM.kt @@ -2,124 +2,64 @@ package de.mm20.launcher2.ui.settings.icons import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory -import de.mm20.launcher2.badges.settings.BadgeSettings import de.mm20.launcher2.icons.IconPack import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.search.Application +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.ui.BadgeSettings +import de.mm20.launcher2.preferences.ui.IconSettings +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.services.favorites.FavoritesService import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.get class IconsSettingsScreenVM( - private val dataStore: LauncherDataStore, + private val uiSettings: UiSettings, + private val iconSettings: IconSettings, private val badgeSettings: BadgeSettings, private val iconService: IconService, private val favoritesService: FavoritesService, private val permissionsManager: PermissionsManager, ) : ViewModel() { + val grid = uiSettings.gridSettings - val columnCount = dataStore.data.map { it.grid.columnCount } fun setColumnCount(columnCount: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setGrid(it.grid.toBuilder().setColumnCount(columnCount)) - .build() - } - } + uiSettings.setGridColumnCount(columnCount) } - val iconSize = dataStore.data.map { it.grid.iconSize } fun setIconSize(iconSize: Int) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setGrid(it.grid.toBuilder().setIconSize(iconSize)) - .build() - } - } + uiSettings.setGridIconSize(iconSize) } - - val showLabels = dataStore.data.map { it.grid.showLabels } fun setShowLabels(showLabels: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setGrid(it.grid.toBuilder().setShowLabels(showLabels)) - .build() - } - } + uiSettings.setGridShowLabels(showLabels) } - val iconShape = dataStore.data.map { it.icons.shape } - fun setIconShape(iconShape: Settings.IconSettings.IconShape) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons.toBuilder() - .setShape(iconShape) - ) - .build() - } - } + val iconShape = uiSettings.iconShape + fun setIconShape(iconShape: IconShape) { + uiSettings.setIconShape(iconShape) } - val adaptifyLegacyIcons = dataStore.data.map { it.icons.adaptify } + val icons = iconSettings fun setAdaptifyLegacyIcons(adaptify: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons.toBuilder() - .setAdaptify(adaptify) - ) - .build() - } - } + iconSettings.setAdaptifyLegacyIcons(adaptify) } - val themedIcons = dataStore.data.map { it.icons.themedIcons } fun setThemedIcons(themedIcons: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons.toBuilder() - .setThemedIcons(themedIcons) - ) - .build() - } - } + iconSettings.setThemedIcons(themedIcons) } - val forceThemedIcons = dataStore.data.map { it.icons.forceThemed } fun setForceThemedIcons(forceThemedIcons: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons.toBuilder() - .setForceThemed(forceThemedIcons) - ) - .build() - } - } + iconSettings.setForceThemedIcons(forceThemedIcons) } val installedIconPacks: Flow> = iconService.getInstalledIconPacks().map { @@ -132,33 +72,12 @@ class IconsSettingsScreenVM( ) + it } - val iconPackThemed = dataStore.data.map { it.icons.iconPackThemed } fun setIconPackThemed(iconPackThemed: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons - .toBuilder() - .setIconPackThemed(iconPackThemed) - ) - .build() - } - } + iconSettings.setIconPackThemed(iconPackThemed) } - val iconPack = dataStore.data.map { it.icons.iconPack } - fun setIconPack(iconPack: String) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setIcons( - it.icons.toBuilder() - .setIconPack(iconPack) - ) - .build() - } - } + fun setIconPack(iconPack: String?) { + iconSettings.setIconPack(iconPack?.takeIf { it.isNotBlank() }) } val hasNotificationsPermission = permissionsManager.hasPermission(PermissionGroup.Notifications) @@ -193,10 +112,10 @@ class IconsSettingsScreenVM( } fun getPreviewIcons(size: Int): Flow> { - return columnCount.flatMapLatest { cols -> + return grid.flatMapLatest { grid -> favoritesService.getFavorites( includeTypes = listOf("app"), - limit = cols, + limit = grid.columnCount, manuallySorted = true, automaticallySorted = true, frequentlyUsed = true, @@ -214,11 +133,12 @@ class IconsSettingsScreenVM( val Factory = viewModelFactory { initializer { IconsSettingsScreenVM( - dataStore = get(), + uiSettings = get(), iconService = get(), permissionsManager = get(), favoritesService = get(), badgeSettings = get(), + iconSettings = get(), ) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/media/MediaIntegrationSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/media/MediaIntegrationSettingsScreenVM.kt index 9afe8dba..af895dd1 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/media/MediaIntegrationSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/media/MediaIntegrationSettingsScreenVM.kt @@ -11,13 +11,12 @@ import de.mm20.launcher2.ktx.normalize import de.mm20.launcher2.music.MusicService import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.media.MediaSettings import de.mm20.launcher2.search.AppProfile import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent @@ -29,7 +28,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent { private val musicService: MusicService by inject() private val appRepository: AppRepository by inject() private val iconService: IconService by inject() - private val dataStore: LauncherDataStore by inject() + + private val mediaSettings: MediaSettings by inject() + val hasPermission = permissionsManager.hasPermission(PermissionGroup.Notifications) @@ -52,9 +53,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent { val musicApps = musicService.getInstalledPlayerPackages() val allApps = appRepository.findMany().first().filter { it.profile == AppProfile.Personal } .distinctBy { it.componentName.packageName } - val settings = dataStore.data.map { it.musicWidget }.first() - val allowList = settings.allowListList - val denyList = settings.denyListList + val settings = mediaSettings.first() + val allowList = settings.allowList + val denyList = settings.denyList appList.value = allApps.map { AppListItem( @@ -92,19 +93,7 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent { denyList.add(app.packageName) } } - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setMusicWidget( - it.musicWidget.toBuilder() - .clearAllowList() - .addAllAllowList(allowList) - .clearDenyList() - .addAllDenyList(denyList) - ) - .build() - } - } + mediaSettings.setLists(allowList.toSet(), denyList.toSet()) } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt index c05483a5..00b7cc87 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreenVM.kt @@ -7,14 +7,14 @@ import android.net.Uri import android.provider.Settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.files.settings.FileSearchSettings import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.plugin.PluginPackage import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginWithState -import de.mm20.launcher2.weather.settings.WeatherSettings +import de.mm20.launcher2.preferences.search.FileSearchSettings +import de.mm20.launcher2.preferences.weather.WeatherSettings import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -111,6 +111,6 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent { val weatherProvider = weatherSettings.providerId fun setWeatherProvider(providerId: String) { - weatherSettings.setProviderId(providerId) + weatherSettings.setProvider(providerId) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt index 871e7e7b..faefb804 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreen.kt @@ -13,7 +13,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.SearchResultOrder import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.preferences.* @@ -232,8 +233,8 @@ fun SearchSettingsScreen() { ListPreference( title = stringResource(R.string.preference_search_result_ordering), items = listOf( - stringResource(R.string.preference_search_result_ordering_alphabetic) to Settings.SearchResultOrderingSettings.Ordering.Alphabetic, - stringResource(R.string.preference_search_result_ordering_weighted) to Settings.SearchResultOrderingSettings.Ordering.Weighted + stringResource(R.string.preference_search_result_ordering_alphabetic) to SearchResultOrder.Alphabetical, + stringResource(R.string.preference_search_result_ordering_weighted) to SearchResultOrder.Weighted ), value = searchResultOrdering, onValueChanged = { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt index 5715c138..a0c377e3 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/search/SearchSettingsScreenVM.kt @@ -5,10 +5,16 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.SearchResultOrder +import de.mm20.launcher2.preferences.search.CalculatorSearchSettings +import de.mm20.launcher2.preferences.search.CalendarSearchSettings +import de.mm20.launcher2.preferences.search.ContactSearchSettings +import de.mm20.launcher2.preferences.search.ShortcutSearchSettings +import de.mm20.launcher2.preferences.search.UnitConverterSettings +import de.mm20.launcher2.preferences.search.WebsiteSearchSettings +import de.mm20.launcher2.preferences.search.WikipediaSearchSettings +import de.mm20.launcher2.preferences.ui.SearchUiSettings import kotlinx.coroutines.flow.SharingStarted - import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -16,35 +22,32 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject class SearchSettingsScreenVM : ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val searchUiSettings: SearchUiSettings by inject() + private val contactSearchSettings: ContactSearchSettings by inject() + private val calendarSearchSettings: CalendarSearchSettings by inject() + private val shortcutSearchSettings: ShortcutSearchSettings by inject() + private val wikipediaSearchSettings: WikipediaSearchSettings by inject() + private val websiteSearchSettings: WebsiteSearchSettings by inject() + private val unitConverterSettings: UnitConverterSettings by inject() + private val calculatorSearchSettings: CalculatorSearchSettings by inject() + private val permissionsManager: PermissionsManager by inject() - val favorites = dataStore.data.map { it.favorites.enabled } + val favorites = searchUiSettings.favorites .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setFavorites(favorites: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setFavorites( - it.favorites.toBuilder().setEnabled(favorites) - ).build() - } - } + searchUiSettings.setFavorites(favorites) } val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val contacts = dataStore.data.map { it.contactsSearch.enabled } + val contacts = contactSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setContacts(contacts: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setContactsSearch( - it.contactsSearch.toBuilder().setEnabled(contacts) - ).build() - } - } + contactSearchSettings.setEnabled(contacts) } fun requestContactsPermission(activity: AppCompatActivity) { @@ -53,133 +56,81 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent { val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val calendar = dataStore.data.map { it.calendarSearch.enabled } + val calendar = calendarSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setCalendar(calendar: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setCalendarSearch( - it.calendarSearch.toBuilder().setEnabled(calendar) - ).build() - } - } + calendarSearchSettings.setEnabled(calendar) } fun requestCalendarPermission(activity: AppCompatActivity) { permissionsManager.requestPermission(activity, PermissionGroup.Calendar) } - val calculator = dataStore.data.map { it.calculatorSearch.enabled } + val calculator = calculatorSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setCalculator(calculator: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setCalculatorSearch( - it.calculatorSearch.toBuilder().setEnabled(calculator) - ).build() - } - } + calculatorSearchSettings.setEnabled(calculator) } - val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled } + val unitConverter = unitConverterSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setUnitConverter(unitConverter: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setUnitConverterSearch( - it.unitConverterSearch.toBuilder().setEnabled(unitConverter) - ).build() - } - } + unitConverterSettings.setEnabled(unitConverter) } - val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled } + val wikipedia = wikipediaSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setWikipedia(wikipedia: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setWikipediaSearch( - it.wikipediaSearch.toBuilder().setEnabled(wikipedia) - ).build() - } - } + wikipediaSearchSettings.setEnabled(wikipedia) } - val websites = dataStore.data.map { it.websiteSearch.enabled } + val websites = websiteSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setWebsites(websites: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setWebsiteSearch( - it.websiteSearch.toBuilder().setEnabled(websites) - ).build() - } - } + websiteSearchSettings.setEnabled(websites) } - val autoFocus = dataStore.data.map { it.searchBar.autoFocus } + val autoFocus = searchUiSettings.openKeyboard .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setAutoFocus(autoFocus: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setSearchBar( - it.searchBar.toBuilder().setAutoFocus(autoFocus) - ).build() - } - } + searchUiSettings.setOpenKeyboard(autoFocus) } - val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter } + val launchOnEnter = searchUiSettings.launchOnEnter .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setLaunchOnEnter(launchOnEnter: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setSearchBar( - it.searchBar.toBuilder().setLaunchOnEnter(launchOnEnter) - ).build() - } - } + searchUiSettings.setLaunchOnEnter(launchOnEnter) } val hasAppShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val appShortcuts = dataStore.data.map { it.appShortcutSearch.enabled } + val appShortcuts = shortcutSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setAppShortcuts(appShortcuts: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setAppShortcutSearch( - it.appShortcutSearch.toBuilder().setEnabled(appShortcuts) - ).build() - } - } + shortcutSearchSettings.setEnabled(appShortcuts) } - val searchResultOrdering = dataStore.data.map { it.resultOrdering.ordering } + val searchResultOrdering = searchUiSettings.resultOrder .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setSearchResultOrdering(searchResultOrdering: Settings.SearchResultOrderingSettings.Ordering) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder().setResultOrdering( - it.resultOrdering.toBuilder().setOrdering(searchResultOrdering) - ).build() - } - } + + fun setSearchResultOrdering(searchResultOrdering: SearchResultOrder) { + searchUiSettings.setResultOrder(searchResultOrdering) } - val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults } + val reverseSearchResults = searchUiSettings.reversedResults .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + fun setReverseSearchResults(reverseSearchResults: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setLayout(it.layout.toBuilder().setReverseSearchResults(reverseSearchResults)) - .build() - } - } + searchUiSettings.setReversedResults(reverseSearchResults) } fun requestAppShortcutsPermission(activity: AppCompatActivity) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt index 16ec997b..1d4f3e6b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/unitconverter/UnitConverterSettingsScreenVM.kt @@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.unitconverter import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.UnitConverterSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -12,35 +12,17 @@ import org.koin.core.component.inject class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val settings: UnitConverterSettings by inject() - val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled } + val unitConverter = settings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setUnitConverter(unitConverter: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setUnitConverterSearch( - it.unitConverterSearch.toBuilder() - .setEnabled(unitConverter) - ) - .build() - } - } + settings.setEnabled(unitConverter) } - val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies } + val currencyConverter = settings.currencies .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setCurrencyConverter(currencyConverter: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setUnitConverterSearch( - it.unitConverterSearch.toBuilder() - .setCurrencies(currencyConverter) - ) - .build() - } - } + settings.setCurrencies(currencyConverter) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt index dab574aa..83807200 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreen.kt @@ -24,7 +24,6 @@ import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.preferences.* -import de.mm20.launcher2.weather.WeatherLocation import de.mm20.launcher2.weather.WeatherProviderInfo @Composable diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt index 32f28032..26d1f409 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/weather/WeatherIntegrationSettingsScreenVM.kt @@ -1,26 +1,18 @@ package de.mm20.launcher2.ui.settings.weather import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.plugins.PluginService -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.weather.WeatherLocation -import de.mm20.launcher2.weather.WeatherProviderInfo +import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.weather.WeatherRepository -import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.* import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMap import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -30,29 +22,22 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent { private val weatherSettings: WeatherSettings by inject() private val pluginService: PluginService by inject() private val permissionsManager: PermissionsManager by inject() - private val dataStore: LauncherDataStore by inject() val availableProviders = repository.getProviders() val weatherProvider = weatherSettings.providerId .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setWeatherProvider(provider: String) { - weatherSettings.setProviderId(provider) + weatherSettings.setProvider(provider) } val weatherProviderPluginState = weatherProvider.flatMapLatest { it?.let { pluginService.getPluginWithState(it) } ?: flowOf(null) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val imperialUnits = dataStore.data.map { it.weather.imperialUnits } + val imperialUnits = weatherSettings.imperialUnits fun setImperialUnits(imperialUnits: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setWeather(it.weather.toBuilder().setImperialUnits(imperialUnits)) - .build() - } - } + weatherSettings.setImperialUnits(imperialUnits) } val autoLocation = weatherSettings.autoLocation diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt index e78119a0..686050a9 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt @@ -24,20 +24,10 @@ fun WikipediaSettingsScreen() { viewModel.setWikipedia(it) } ) - val images by viewModel.images.collectAsState() - SwitchPreference( - title = stringResource(R.string.preference_search_wikipedia_pictures), - summary = stringResource(R.string.preference_search_wikipedia_pictures_summary), - enabled = wikipedia == true, - value = images == true, - onValueChanged = { - viewModel.setImages(it) - } - ) val customUrl by viewModel.customUrl.collectAsState() TextPreference( title = stringResource(R.string.preference_wikipedia_customurl), - value = customUrl, + value = customUrl ?: "", placeholder = stringResource(id = R.string.wikipedia_url), summary = customUrl.takeIf { !it.isNullOrBlank() } ?: stringResource(id = R.string.wikipedia_url), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt index 89c32577..a5ac66b4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreenVM.kt @@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.wikipedia import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.WikipediaSearchSettings import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -11,50 +11,17 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject class WikipediaSettingsScreenVM: ViewModel(), KoinComponent { - private val dataStore: LauncherDataStore by inject() + private val wikipediaSearchSettings: WikipediaSearchSettings by inject() - val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled } + val wikipedia = wikipediaSearchSettings.enabled .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) fun setWikipedia(wikipedia: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setWikipediaSearch( - it.wikipediaSearch.toBuilder() - .setEnabled(wikipedia) - ) - .build() - } - } + wikipediaSearchSettings.setEnabled(wikipedia) } - val images = dataStore.data.map { it.wikipediaSearch.images } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - fun setImages(images: Boolean) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setWikipediaSearch( - it.wikipediaSearch.toBuilder() - .setImages(images) - ) - .build() - } - } - } - - val customUrl = dataStore.data.map { it.wikipediaSearch.customUrl } + val customUrl = wikipediaSearchSettings.customUrl .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") fun setCustomUrl(customUrl: String) { - viewModelScope.launch { - dataStore.updateData { - it.toBuilder() - .setWikipediaSearch( - it.wikipediaSearch.toBuilder() - .setCustomUrl(customUrl) - ) - .build() - } - } + wikipediaSearchSettings.setCustomUrl(customUrl) } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt index 67859341..cb0b716b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt @@ -1,23 +1,18 @@ package de.mm20.launcher2.ui.theme import android.content.Context -import android.os.Build -import android.util.Log import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.coerceAtMost import androidx.compose.ui.unit.dp -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.AppearanceSettings -import de.mm20.launcher2.themes.DefaultThemeId -import de.mm20.launcher2.themes.Theme +import de.mm20.launcher2.preferences.Font +import de.mm20.launcher2.preferences.SurfaceShape +import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.theme.colorscheme.* @@ -26,7 +21,7 @@ import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import org.koin.androidx.compose.inject -import java.util.UUID +import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref @Composable @@ -35,33 +30,31 @@ fun LauncherTheme( ) { val context = LocalContext.current - val dataStore: LauncherDataStore by inject() + val uiSettings: UiSettings by inject() val themeRepository: ThemeRepository by inject() val theme by remember { - dataStore.data.map { - it.appearance.themeId.takeIf { it.isNotEmpty() }?.let { - UUID.fromString(it) - } - }.flatMapLatest { + uiSettings.theme.flatMapLatest { themeRepository.getThemeOrDefault(it) } }.collectAsState(themeRepository.getDefaultTheme()) - val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState( - AppearanceSettings.Theme.System + val colorSchemePref by remember { uiSettings.colorScheme }.collectAsState( + ColorSchemePref.System ) val darkTheme = - themePreference == AppearanceSettings.Theme.Dark || themePreference == AppearanceSettings.Theme.System && isSystemInDarkTheme() + colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme() val cornerRadius by remember { - dataStore.data.map { it.cards.radius.dp } + uiSettings.cardStyle.map { + it.cornerRadius.dp + } }.collectAsState(8.dp) val baseShape by remember { - dataStore.data.map { - when (it.cards.shape) { - Settings.CardSettings.Shape.Cut -> CutCornerShape(0f) + uiSettings.cardStyle.map { + when (it.shape) { + SurfaceShape.Cut -> CutCornerShape(0f) else -> RoundedCornerShape(0f) } } @@ -73,8 +66,8 @@ fun LauncherTheme( lightColorSchemeOf(theme) } - val font by remember { dataStore.data.map { it.appearance.font } }.collectAsState( - AppearanceSettings.Font.Outfit + val font by remember { uiSettings.font }.collectAsState( + Font.Outfit ) val typography = remember(font) { @@ -99,9 +92,9 @@ fun LauncherTheme( } } -fun getTypography(context: Context, font: AppearanceSettings.Font?): Typography { +fun getTypography(context: Context, font: Font?): Typography { return when (font) { - AppearanceSettings.Font.SystemDefault -> getDeviceDefaultTypography(context) + Font.System -> getDeviceDefaultTypography(context) else -> DefaultTypography } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt index 0de0bbb1..fb89602b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt @@ -8,7 +8,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import de.mm20.launcher2.themes.CorePalette import de.mm20.launcher2.themes.DefaultDarkColorScheme import de.mm20.launcher2.themes.DefaultLightColorScheme @@ -102,7 +102,7 @@ fun systemCorePalette(): CorePalette { } } -fun CustomColorScheme(colors: Settings.AppearanceSettings.CustomColors.Scheme): ColorScheme { +fun CustomColorScheme(colors: LegacySettings.AppearanceSettings.CustomColors.Scheme): ColorScheme { return ColorScheme( primary = Color(colors.primary), onPrimary = Color(colors.onPrimary), diff --git a/core/preferences/build.gradle.kts b/core/preferences/build.gradle.kts index 3c53e0ac..c4350aa9 100644 --- a/core/preferences/build.gradle.kts +++ b/core/preferences/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) alias(libs.plugins.protobuf) + alias(libs.plugins.kotlin.plugin.serialization) } android { @@ -57,6 +58,7 @@ protobuf { dependencies { implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.serialization.json) implementation(libs.androidx.core) implementation(libs.androidx.appcompat) api(libs.androidx.datastore) diff --git a/core/preferences/consumer-rules.pro b/core/preferences/consumer-rules.pro index 16c55696..08ec5a20 100644 --- a/core/preferences/consumer-rules.pro +++ b/core/preferences/consumer-rules.pro @@ -1,7 +1,7 @@ --keepclassmembers class de.mm20.launcher2.preferences.Settings { +-keepclassmembers class de.mm20.launcher2.preferences.LegacySettings { ; } --keepclassmembers class de.mm20.launcher2.preferences.Settings$* { +-keepclassmembers class de.mm20.launcher2.preferences.LegacySettings$* { ; } diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt deleted file mode 100644 index e1608ccb..00000000 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt +++ /dev/null @@ -1,48 +0,0 @@ -package de.mm20.launcher2.preferences - -import android.content.Context -import androidx.datastore.core.DataMigration -import androidx.datastore.core.DataStore -import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler -import androidx.datastore.dataStore -import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.preferences.migrations.* - -typealias LauncherDataStore = DataStore - -internal val Context.dataStore: LauncherDataStore by dataStore( - fileName = "settings.pb", - serializer = SettingsSerializer, - produceMigrations = { - getMigrations(it) - }, - corruptionHandler = ReplaceFileCorruptionHandler { - CrashReporter.logException(it) - Settings.getDefaultInstance() - } -) - -internal const val SchemaVersion = 18 - -internal fun getMigrations(context: Context): List> { - return listOf( - FactorySettingsMigration(context), - Migration_1_2(), - Migration_2_3(), - Migration_3_4(), - Migration_4_5(), - Migration_5_6(), - Migration_6_7(), - Migration_7_8(), - Migration_8_9(), - Migration_9_10(), - Migration_10_11(), - Migration_11_12(), - Migration_12_13(), - Migration_13_14(), - Migration_14_15(), - Migration_15_16(), - Migration_16_17(), - Migration_17_18(), - ) -} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt index 2ba95c97..fdb9d706 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt @@ -2,58 +2,58 @@ package de.mm20.launcher2.preferences import android.content.Context import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors +import de.mm20.launcher2.preferences.LegacySettings.SearchBarSettings.SearchBarColors import de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme import scheme.Scheme import java.util.UUID -fun createFactorySettings(context: Context): Settings { - return Settings.newBuilder() +fun createFactorySettings(context: Context): LegacySettings { + return LegacySettings.newBuilder() .setAppearance( - Settings.AppearanceSettings + LegacySettings.AppearanceSettings .newBuilder() - .setTheme(Settings.AppearanceSettings.Theme.System) + .setTheme(LegacySettings.AppearanceSettings.Theme.System) .setDimWallpaper(false) .setBlurWallpaper(true) .setBlurWallpaperRadius(32) .setThemeId(UUID(0L, 0L).toString()) - .setFont(Settings.AppearanceSettings.Font.Outfit) + .setFont(LegacySettings.AppearanceSettings.Font.Outfit) .build() ) .setWeather( - Settings.WeatherSettings + LegacySettings.WeatherSettings .newBuilder() - .setProvider(Settings.WeatherSettings.WeatherProvider.MetNo) + .setProvider(LegacySettings.WeatherSettings.WeatherProvider.MetNo) .setImperialUnits(context.resources.getBoolean(R.bool.default_imperialUnits)) .build() ) .setMusicWidget( - Settings.MusicWidgetSettings + LegacySettings.MusicWidgetSettings .newBuilder() .build() ) .setCalendarWidget( - Settings.CalendarWidgetSettings + LegacySettings.CalendarWidgetSettings .newBuilder() .setHideAlldayEvents(false) ) .setClockWidget( - Settings.ClockWidgetSettings + LegacySettings.ClockWidgetSettings .newBuilder() - .setLayout(Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) - .setClockStyle(Settings.ClockWidgetSettings.ClockStyle.DigitalClock1) - .setColor(Settings.ClockWidgetSettings.ClockWidgetColors.Auto) + .setLayout(LegacySettings.ClockWidgetSettings.ClockWidgetLayout.Vertical) + .setClockStyle(LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1) + .setColor(LegacySettings.ClockWidgetSettings.ClockWidgetColors.Auto) .setAlarmPart(true) .setBatteryPart(true) .setMusicPart(true) .setDatePart(true) .setFavoritesPart(false) .setFillHeight(true) - .setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) + .setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) .build() ) .setFavorites( - Settings.FavoritesSettings + LegacySettings.FavoritesSettings .newBuilder() .setEnabled(true) .setFrequentlyUsed(true) @@ -61,7 +61,7 @@ fun createFactorySettings(context: Context): Settings { .setEditButton(true) ) .setFileSearch( - Settings.FilesSearchSettings + LegacySettings.FilesSearchSettings .newBuilder() .setLocalFiles(true) .setNextcloud(false) @@ -70,50 +70,50 @@ fun createFactorySettings(context: Context): Settings { .setNextcloud(false) ) .setContactsSearch( - Settings.ContactsSearchSettings + LegacySettings.ContactsSearchSettings .newBuilder() .setEnabled(true) ) .setCalendarSearch( - Settings.CalendarSearchSettings + LegacySettings.CalendarSearchSettings .newBuilder() .setEnabled(true) ) .setAppShortcutSearch( - Settings.AppShortcutSearchSettings + LegacySettings.AppShortcutSearchSettings .newBuilder() .setEnabled(true) ) .setCalculatorSearch( - Settings.CalculatorSearchSettings + LegacySettings.CalculatorSearchSettings .newBuilder() .setEnabled(true) ) .setUnitConverterSearch( - Settings.UnitConverterSearchSettings + LegacySettings.UnitConverterSearchSettings .newBuilder() .setEnabled(true) .setCurrencies(true) ) .setWikipediaSearch( - Settings.WikipediaSearchSettings + LegacySettings.WikipediaSearchSettings .newBuilder() .setEnabled(false) .setImages(false) .setCustomUrl("") ) .setWebsiteSearch( - Settings.WebsiteSearchSettings + LegacySettings.WebsiteSearchSettings .newBuilder() .setEnabled(false) ) .setWebSearch( - Settings.WebSearchSettings + LegacySettings.WebSearchSettings .newBuilder() .setEnabled(true) ) .setBadges( - Settings.BadgeSettings + LegacySettings.BadgeSettings .newBuilder() .setNotifications(true) .setCloudFiles(true) @@ -121,15 +121,15 @@ fun createFactorySettings(context: Context): Settings { .setSuspendedApps(true) ) .setGrid( - Settings.GridSettings.newBuilder() + LegacySettings.GridSettings.newBuilder() .setColumnCount(context.resources.getInteger(R.integer.config_columnCount)) .setIconSize(48) .setShowLabels(true) .build() ) .setSearchBar( - Settings.SearchBarSettings.newBuilder() - .setSearchBarStyle(Settings.SearchBarSettings.SearchBarStyle.Transparent) + LegacySettings.SearchBarSettings.newBuilder() + .setSearchBarStyle(LegacySettings.SearchBarSettings.SearchBarStyle.Transparent) .setAutoFocus(true) .setLaunchOnEnter(true) .setColor(SearchBarColors.Auto) @@ -137,67 +137,67 @@ fun createFactorySettings(context: Context): Settings { .build() ) .setIcons( - Settings.IconSettings.newBuilder() + LegacySettings.IconSettings.newBuilder() .setAdaptify(true) - .setShape(Settings.IconSettings.IconShape.PlatformDefault) + .setShape(LegacySettings.IconSettings.IconShape.PlatformDefault) .setThemedIcons(false) .setIconPack("") .setIconPackThemed(true) ) .setEasterEgg(false) .setSystemBars( - Settings.SystemBarsSettings.newBuilder() - .setNavBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto) - .setStatusBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto) + LegacySettings.SystemBarsSettings.newBuilder() + .setNavBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto) + .setStatusBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto) .setHideStatusBar(false) .setHideNavBar(false) ) .setCards( - Settings.CardSettings.newBuilder() + LegacySettings.CardSettings.newBuilder() .setBorderWidth(0) .setRadius(12) .setOpacity(1f) ) .setWidgets( - Settings.WidgetSettings.newBuilder() + LegacySettings.WidgetSettings.newBuilder() .setEditButton(true) ) .setLayout( - Settings.LayoutSettings.newBuilder() - .setBaseLayout(Settings.LayoutSettings.Layout.PullDown) + LegacySettings.LayoutSettings.newBuilder() + .setBaseLayout(LegacySettings.LayoutSettings.Layout.PullDown) .setBottomSearchBar(false) .setReverseSearchResults(false) .setFixedRotation(false) ) .setGestures( - Settings.GestureSettings.newBuilder() + LegacySettings.GestureSettings.newBuilder() .setDoubleTap( if (isAtLeastApiLevel(28)) { - Settings.GestureSettings.GestureAction.LockScreen + LegacySettings.GestureSettings.GestureAction.LockScreen } else { - Settings.GestureSettings.GestureAction.None + LegacySettings.GestureSettings.GestureAction.None }) - .setLongPress(Settings.GestureSettings.GestureAction.None) - .setSwipeDown(Settings.GestureSettings.GestureAction.OpenNotificationDrawer) - .setSwipeLeft(Settings.GestureSettings.GestureAction.None) - .setSwipeRight(Settings.GestureSettings.GestureAction.None) + .setLongPress(LegacySettings.GestureSettings.GestureAction.None) + .setSwipeDown(LegacySettings.GestureSettings.GestureAction.OpenNotificationDrawer) + .setSwipeLeft(LegacySettings.GestureSettings.GestureAction.None) + .setSwipeRight(LegacySettings.GestureSettings.GestureAction.None) ) .setResultOrdering( - Settings.SearchResultOrderingSettings.newBuilder() - .setOrdering(Settings.SearchResultOrderingSettings.Ordering.Weighted) - .setWeightFactor(Settings.SearchResultOrderingSettings.WeightFactor.Default) + LegacySettings.SearchResultOrderingSettings.newBuilder() + .setOrdering(LegacySettings.SearchResultOrderingSettings.Ordering.Weighted) + .setWeightFactor(LegacySettings.SearchResultOrderingSettings.WeightFactor.Default) ) .setAnimations( - Settings.AnimationSettings.newBuilder() + LegacySettings.AnimationSettings.newBuilder() .setCharging(true) ) .build() } -internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.BaseColors +internal val DefaultCustomColorsBase: LegacySettings.AppearanceSettings.CustomColors.BaseColors get() { val scheme = Scheme.light(0xFFACE330.toInt()) - return Settings.AppearanceSettings.CustomColors.BaseColors.newBuilder() + return LegacySettings.AppearanceSettings.CustomColors.BaseColors.newBuilder() .setAccent1(scheme.primary) .setAccent2(scheme.secondary) .setAccent3(scheme.tertiary) @@ -207,13 +207,13 @@ internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.B .build() } -internal val DefaultLightCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme +internal val DefaultLightCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme get() { val scheme = Scheme.light(0xFFACE330.toInt()) return scheme.toSettingsColorsScheme() } -internal val DefaultDarkCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme +internal val DefaultDarkCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme get() { val scheme = Scheme.dark(0xFFACE330.toInt()) return scheme.toSettingsColorsScheme() diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt index 0a5a051a..f29b823b 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt @@ -14,7 +14,7 @@ import java.io.File internal class LauncherStoreBackupComponent( private val context: Context, - private val dataStore: LauncherDataStore + private val dataStore: LegacyDataStore ): Backupable { override suspend fun backup(toDir: File) { dataStore.export(toDir) @@ -25,10 +25,10 @@ internal class LauncherStoreBackupComponent( } } -suspend fun LauncherDataStore.export(toDir: File) { +suspend fun LegacyDataStore.export(toDir: File) { val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) val backupDataStore = DataStoreFactory.create( - serializer = SettingsSerializer, + serializer = LegacySettingsSerializer, produceFile = { File(toDir, "settings") }, @@ -42,14 +42,14 @@ suspend fun LauncherDataStore.export(toDir: File) { } -suspend fun LauncherDataStore.import(context: Context, fromDir: File) { +suspend fun LegacyDataStore.import(context: Context, fromDir: File) { val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) val backupDataStore = DataStoreFactory.create( - serializer = SettingsSerializer, + serializer = LegacySettingsSerializer, migrations = getMigrations(context), corruptionHandler = ReplaceFileCorruptionHandler { CrashReporter.logException(it) - Settings.getDefaultInstance() + LegacySettings.getDefaultInstance() }, produceFile = { File(fromDir, "settings") diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherDataStore.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherDataStore.kt new file mode 100644 index 00000000..5b2e47d7 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherDataStore.kt @@ -0,0 +1,23 @@ +package de.mm20.launcher2.preferences + +import android.content.Context +import de.mm20.launcher2.preferences.migrations.Migration1 +import de.mm20.launcher2.settings.BaseSettings + +internal class LauncherDataStore( + private val context: Context, + legacyDataStore: LegacyDataStore, +): BaseSettings( + context, + fileName = "settings.json", + serializer = LauncherSettingsDataSerializer, + migrations = listOf(Migration1(legacyDataStore)), +) { + + val data + get() = context.dataStore.data + + fun update(block: (LauncherSettingsData) -> LauncherSettingsData) { + updateData(block) + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt new file mode 100644 index 00000000..e7886204 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt @@ -0,0 +1,332 @@ +package de.mm20.launcher2.preferences + +import android.content.Context +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.util.UUID + +@Serializable +data class LauncherSettingsData( + val schemaVersion: Int = 1, + + val uiColorScheme: ColorScheme = ColorScheme.System, + val uiTheme: ThemeDescriptor = ThemeDescriptor.Default, + val uiFont: Font = Font.Outfit, + val uiBaseLayout: BaseLayout = BaseLayout.PullDown, + val uiOrientation: ScreenOrientation = ScreenOrientation.Auto, + + val wallpaperDim: Boolean = false, + val wallpaperBlur: Boolean = true, + val wallpaperBlurRadius: Int = 32, + + val mediaAllowList: Set = emptySet(), + val mediaDenyList: Set = emptySet(), + + val clockWidgetCompact: Boolean = false, + val clockWidgetStyle: ClockWidgetStyle = ClockWidgetStyle.Digital1(), + val clockWidgetColors: ClockWidgetColors = ClockWidgetColors.Auto, + val clockWidgetAlarmPart: Boolean = true, + val clockWidgetBatteryPart: Boolean = true, + val clockWidgetMusicPart: Boolean = true, + val clockWidgetDatePart: Boolean = true, + val clockWidgetFillHeight: Boolean = true, + val clockWidgetAlignment: ClockWidgetAlignment = ClockWidgetAlignment.Bottom, + + val homeScreenDock: Boolean = false, + + val favoritesEnabled: Boolean = true, + val favoritesFrequentlyUsed: Boolean = true, + val favoritesFrequentlyUsedRows: Int = 1, + val favoritesEditButton: Boolean = true, + + val fileSearchProviders: Set = setOf("local"), + + val contactSearchEnabled: Boolean = true, + + val calendarSearchEnabled: Boolean = true, + + val shortcutSearchEnabled: Boolean = true, + + val calculatorEnabled: Boolean = true, + + val unitConverterEnabled: Boolean = true, + val unitConverterCurrencies: Boolean = true, + + val wikipediaSearchEnabled: Boolean = false, + val wikipediaSearchImages: Boolean = false, + val wikipediaCustomUrl: String? = null, + + val websiteSearchEnabled: Boolean = false, + + val badgesNotifications: Boolean = true, + val badgesSuspendedApps: Boolean = true, + val badgesCloudFiles: Boolean = true, + val badgesShortcuts: Boolean = true, + val badgesPlugins: Boolean = true, + + val gridColumnCount: Int = 5, + val gridIconSize: Int = 48, + val gridLabels: Boolean = true, + + val searchBarStyle: SearchBarStyle = SearchBarStyle.Transparent, + val searchBarColors: SearchBarColors = SearchBarColors.Auto, + val searchBarKeyboard: Boolean = true, + val searchLaunchOnEnter: Boolean = true, + val searchBarBottom: Boolean = false, + val searchBarFixed: Boolean = false, + + val searchResultsReversed: Boolean = false, + val searchResultOrder: SearchResultOrder = SearchResultOrder.Weighted, + + val rankingWeightFactor: WeightFactor = WeightFactor.Default, + + val hiddenItemsShowButton: Boolean = true, + + val iconsShape: IconShape = IconShape.PlatformDefault, + val iconsAdaptify: Boolean = false, + val iconsThemed: Boolean = false, + val iconsForceThemed: Boolean = false, + val iconsPack: String? = null, + val iconsPackThemed: Boolean = false, + + val easterEgg: Boolean = false, + + val systemBarsHideStatus: Boolean = false, + val systemBarsHideNav: Boolean = false, + val systemBarsStatusColors: SystemBarColors = SystemBarColors.Auto, + val systemBarsNavColors: SystemBarColors = SystemBarColors.Auto, + + val surfacesOpacity: Float = 1f, + val surfacesRadius: Int = 24, + val surfacesBorderWidth: Int = 0, + val surfacesShape: SurfaceShape = SurfaceShape.Rounded, + + val widgetsEditButton: Boolean = true, + + val gesturesSwipeDown: GestureAction = GestureAction.Notifications, + val gesturesSwipeLeft: GestureAction = GestureAction.NoAction, + val gesturesSwipeRight: GestureAction = GestureAction.NoAction, + val gesturesDoubleTap: GestureAction = GestureAction.ScreenLock, + val gesturesLongPress: GestureAction = GestureAction.NoAction, + val gesturesHomeButton: GestureAction = GestureAction.NoAction, + + val animationsCharging: Boolean = true, + + val stateTagsMultiline: Boolean = false, + + val weatherProvider: String = "metno", + val weatherAutoLocation: Boolean = true, + val weatherLocation: LatLon? = null, + val weatherLocationName: String? = null, + val weatherLastLocation: LatLon? = null, + val weatherLastUpdate: Long = 0L, + val weatherProviderSettings: Map = emptyMap(), + val weatherImperialUnits: Boolean = false, + + ) { + constructor( + context: Context, + ) : this( + weatherImperialUnits = context.resources.getBoolean(R.bool.default_imperialUnits), + gridColumnCount = context.resources.getInteger(R.integer.config_columnCount), + ) +} + +@Serializable +enum class ColorScheme { + Light, + Dark, + System, +} + +@Serializable +enum class Font { + Outfit, + System, +} + + +@Serializable +sealed interface ThemeDescriptor { + @Serializable + @SerialName("default") + data object Default : ThemeDescriptor + + @Serializable + @SerialName("bw") + data object BlackAndWhite : ThemeDescriptor + + @Serializable + @SerialName("custom") + data class Custom( + val id: String, + ) : ThemeDescriptor +} + +@Serializable +sealed interface ClockWidgetStyle { + @Serializable + @SerialName("digital1") + data class Digital1( + val outlined: Boolean = false, + val variant: Variant = Variant.Default, + ) : ClockWidgetStyle { + @Serializable + enum class Variant { + Default, + MDY, + OnePlus, + } + } + + @Serializable + @SerialName("digital2") + data object Digital2 : ClockWidgetStyle + + @Serializable + @SerialName("orbit") + data object Orbit : ClockWidgetStyle + + @Serializable + @SerialName("analog") + data object Analog : ClockWidgetStyle + + @Serializable + @SerialName("binary") + data object Binary : ClockWidgetStyle + + @Serializable + @SerialName("empty") + data object Empty : ClockWidgetStyle +} + +@Serializable +enum class ClockWidgetColors { + Auto, + Light, + Dark, +} + +@Serializable +enum class ClockWidgetAlignment { + Top, + Center, + Bottom, +} + +@Serializable +enum class SearchBarStyle { + Transparent, + Solid, + Hidden, +} + +@Serializable +enum class SearchBarColors { + Auto, + Light, + Dark, +} + +@Serializable +enum class IconShape { + PlatformDefault, + Circle, + Square, + RoundedSquare, + Triangle, + Squircle, + Hexagon, + Pentagon, + Teardrop, + Pebble, + EasterEgg, +} + +@Serializable +enum class SystemBarColors { + Auto, + Light, + Dark, +} + +@Serializable +enum class SurfaceShape { + Rounded, + Cut, +} + +@Serializable +enum class BaseLayout { + PullDown, + Pager, + PagerReversed, +} + +@Serializable +enum class ScreenOrientation { + Auto, + Portrait, + Landscape, +} + +@Serializable +sealed interface GestureAction { + @Serializable + @SerialName("no_action") + data object NoAction : GestureAction + + @Serializable + @SerialName("notifications") + data object Notifications : GestureAction + + @Serializable + @SerialName("quick_settings") + data object QuickSettings : GestureAction + + @Serializable + @SerialName("screen_lock") + data object ScreenLock : GestureAction + + @Serializable + @SerialName("search") + data object Search : GestureAction + + @Serializable + @SerialName("power_menu") + data object PowerMenu : GestureAction + + @Serializable + @SerialName("recents") + data object Recents : GestureAction + + @Serializable + @SerialName("launch_searchable") + data class Launch(val key: String?) : GestureAction +} + +@Serializable +enum class SearchResultOrder { + Weighted, + Alphabetical, + LaunchCount, +} + +@Serializable +enum class WeightFactor { + Default, + Low, + High, +} + +@Serializable +data class LatLon( + val lat: Double, + val lon: Double, +) + +@Serializable +data class ProviderSettings( + val locationId: String? = null, + val locationName: String? = null, +) \ No newline at end of file diff --git a/data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsDataSerializer.kt similarity index 55% rename from data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettingsData.kt rename to core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsDataSerializer.kt index 1a9849e6..1a32b2d6 100644 --- a/data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettingsData.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsDataSerializer.kt @@ -1,8 +1,7 @@ -package de.mm20.launcher2.files.settings +package de.mm20.launcher2.preferences import androidx.datastore.core.CorruptionException import androidx.datastore.core.Serializer -import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream @@ -11,27 +10,17 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream -@Serializable -data class FileSearchSettingsData( - val localFiles: Boolean = true, - val gdriveFiles: Boolean = false, - val nextcloudFiles: Boolean = false, - val owncloudFiles: Boolean = false, - val plugins: Set = emptySet(), - val schemaVersion: Int = 1, -) - -internal object FileSearchSettingsDataSerializer : Serializer { +internal object LauncherSettingsDataSerializer : Serializer { internal val json = Json { ignoreUnknownKeys = true encodeDefaults = true } - override val defaultValue: FileSearchSettingsData - get() = FileSearchSettingsData(schemaVersion = 0) + override val defaultValue: LauncherSettingsData + get() = LauncherSettingsData(schemaVersion = 0) - override suspend fun readFrom(input: InputStream): FileSearchSettingsData { + override suspend fun readFrom(input: InputStream): LauncherSettingsData { try { return json.decodeFromStream(input) } catch (e: IllegalArgumentException) { @@ -43,7 +32,7 @@ internal object FileSearchSettingsDataSerializer : Serializer + +internal val Context.legacyDataStore: LegacyDataStore by dataStore( + fileName = "settings.pb", + serializer = LegacySettingsSerializer, + produceMigrations = { + getMigrations(it) + }, + corruptionHandler = ReplaceFileCorruptionHandler { + CrashReporter.logException(it) + LegacySettings.getDefaultInstance() + } +) + +internal const val SchemaVersion = 18 + +internal fun getMigrations(context: Context): List> { + return listOf() +} + + + +object LegacySettingsSerializer : Serializer { + override val defaultValue: LegacySettings = LegacySettings.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): LegacySettings { + try { + return LegacySettings.parseFrom(input) + } catch (e: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", e) + } + } + + override suspend fun writeTo(t: LegacySettings, output: OutputStream) { + t.writeTo(output) + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt index 39ae797e..4f3515c5 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt @@ -1,11 +1,51 @@ package de.mm20.launcher2.preferences import de.mm20.launcher2.backup.Backupable +import de.mm20.launcher2.preferences.search.ContactSearchSettings +import de.mm20.launcher2.preferences.media.MediaSettings +import de.mm20.launcher2.preferences.search.CalculatorSearchSettings +import de.mm20.launcher2.preferences.search.CalendarSearchSettings +import de.mm20.launcher2.preferences.search.FavoritesSettings +import de.mm20.launcher2.preferences.search.FileSearchSettings +import de.mm20.launcher2.preferences.search.RankingSettings +import de.mm20.launcher2.preferences.search.ShortcutSearchSettings +import de.mm20.launcher2.preferences.search.UnitConverterSettings +import de.mm20.launcher2.preferences.search.WebsiteSearchSettings +import de.mm20.launcher2.preferences.search.WikipediaSearchSettings +import de.mm20.launcher2.preferences.ui.BadgeSettings +import de.mm20.launcher2.preferences.ui.ClockWidgetSettings +import de.mm20.launcher2.preferences.ui.GestureSettings +import de.mm20.launcher2.preferences.ui.IconSettings +import de.mm20.launcher2.preferences.ui.SearchUiSettings +import de.mm20.launcher2.preferences.ui.UiSettings +import de.mm20.launcher2.preferences.ui.UiState +import de.mm20.launcher2.preferences.weather.WeatherSettings import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.dsl.module val preferencesModule = module { - single { androidContext().dataStore } - factory(named()) { LauncherStoreBackupComponent(androidContext(), get()) } + single { androidContext().legacyDataStore } + factory(named()) { LauncherStoreBackupComponent(androidContext(), get()) } + single { LauncherDataStore(androidContext(), get()) } + factory(named()) { get() } + factory { MediaSettings(get()) } + factory { ContactSearchSettings(get()) } + factory { FileSearchSettings(get()) } + factory { UnitConverterSettings(get()) } + factory { BadgeSettings(get()) } + factory { UiSettings(get()) } + factory { ShortcutSearchSettings(get()) } + factory { FavoritesSettings(get()) } + factory { WikipediaSearchSettings(get()) } + factory { IconSettings(get()) } + factory { RankingSettings(get()) } + factory { CalendarSearchSettings(get()) } + factory { WebsiteSearchSettings(get()) } + factory { UiState(get()) } + factory { SearchUiSettings(get()) } + factory { WeatherSettings(get()) } + factory { GestureSettings(get()) } + factory { CalculatorSearchSettings(get()) } + factory { ClockWidgetSettings(get()) } } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/SettingsSerializer.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/SettingsSerializer.kt deleted file mode 100644 index 56d12cf0..00000000 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/SettingsSerializer.kt +++ /dev/null @@ -1,24 +0,0 @@ -package de.mm20.launcher2.preferences - -import androidx.datastore.core.CorruptionException -import androidx.datastore.core.Serializer -import com.google.protobuf.InvalidProtocolBufferException -import java.io.InputStream -import java.io.OutputStream - - -object SettingsSerializer : Serializer { - override val defaultValue: Settings = Settings.getDefaultInstance() - - override suspend fun readFrom(input: InputStream): Settings { - try { - return Settings.parseFrom(input) - } catch (e: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", e) - } - } - - override suspend fun writeTo(t: Settings, output: OutputStream) { - t.writeTo(output) - } -} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ktx/Scheme.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ktx/Scheme.kt index a79d0240..4aeb847a 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ktx/Scheme.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ktx/Scheme.kt @@ -1,11 +1,11 @@ package de.mm20.launcher2.preferences.ktx -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import scheme.Scheme -fun Scheme.toSettingsColorsScheme(): Settings.AppearanceSettings.CustomColors.Scheme { +fun Scheme.toSettingsColorsScheme(): LegacySettings.AppearanceSettings.CustomColors.Scheme { val scheme = this - return Settings.AppearanceSettings.CustomColors.Scheme.newBuilder() + return LegacySettings.AppearanceSettings.CustomColors.Scheme.newBuilder() .setPrimary(scheme.primary) .setSurfaceTint(scheme.primary) .setOnPrimary(scheme.onPrimary) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/media/MediaSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/media/MediaSettings.kt new file mode 100644 index 00000000..31278ef6 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/media/MediaSettings.kt @@ -0,0 +1,31 @@ +package de.mm20.launcher2.preferences.media + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +data class MediaSettingsData( + val allowList: Set, + val denyList: Set, +) + +class MediaSettings internal constructor( + private val launcherDataStore: LauncherDataStore +) : Flow by (launcherDataStore.data.map { + MediaSettingsData( + it.mediaAllowList, + it.mediaDenyList + ) +}) { + val allowList + get() = launcherDataStore.data.map { it.mediaAllowList } + + fun setLists(allowList: Set, denyList: Set) { + launcherDataStore.update { + it.copy(mediaAllowList = allowList) + } + } + + val denyList + get() = launcherDataStore.data.map { it.mediaDenyList } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/FactorySettingsMigration.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/FactorySettingsMigration.kt index a5d63e72..7bb5833e 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/FactorySettingsMigration.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/FactorySettingsMigration.kt @@ -4,22 +4,22 @@ import android.content.Context import android.util.Log import androidx.datastore.core.DataMigration import de.mm20.launcher2.preferences.SchemaVersion -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import de.mm20.launcher2.preferences.createFactorySettings -class FactorySettingsMigration(private val context: Context): DataMigration { +class FactorySettingsMigration(private val context: Context): DataMigration { override suspend fun cleanUp() { } - override suspend fun migrate(currentData: Settings): Settings { + override suspend fun migrate(currentData: LegacySettings): LegacySettings { Log.d("MM20", "Initializing user settings…") Log.d("MM20", "Done") val defaults = createFactorySettings(context) return defaults.toBuilder().setVersion(SchemaVersion).build() } - override suspend fun shouldMigrate(currentData: Settings): Boolean { + override suspend fun shouldMigrate(currentData: LegacySettings): Boolean { return currentData.version == 0 } } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration1.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration1.kt new file mode 100644 index 00000000..ca315ec7 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration1.kt @@ -0,0 +1,254 @@ +package de.mm20.launcher2.preferences.migrations + +import androidx.datastore.core.DataMigration +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.ClockWidgetAlignment +import de.mm20.launcher2.preferences.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.ColorScheme +import de.mm20.launcher2.preferences.Font +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.LauncherSettingsData +import de.mm20.launcher2.preferences.LegacyDataStore +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.ScreenOrientation +import de.mm20.launcher2.preferences.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarStyle +import de.mm20.launcher2.preferences.SearchResultOrder +import de.mm20.launcher2.preferences.SurfaceShape +import de.mm20.launcher2.preferences.SystemBarColors +import de.mm20.launcher2.preferences.ThemeDescriptor +import de.mm20.launcher2.preferences.WeightFactor +import kotlinx.coroutines.flow.first +import java.util.UUID + +class Migration1( + private val legacyDataStore: LegacyDataStore, +) : DataMigration { + override suspend fun cleanUp() { + + } + + override suspend fun shouldMigrate(currentData: LauncherSettingsData): Boolean { + return legacyDataStore.data.first().version > 0 && currentData.schemaVersion < 1 + } + + override suspend fun migrate(currentData: LauncherSettingsData): LauncherSettingsData { + val legacyData = legacyDataStore.data.first() + return currentData.copy( + schemaVersion = 1, + uiBaseLayout = when (legacyData.layout.baseLayout) { + LegacySettings.LayoutSettings.Layout.Pager -> BaseLayout.PullDown + LegacySettings.LayoutSettings.Layout.PagerReversed -> BaseLayout.PagerReversed + else -> BaseLayout.PullDown + }, + gridColumnCount = legacyData.grid.columnCount, + animationsCharging = legacyData.animations.charging, + badgesCloudFiles = legacyData.badges.cloudFiles, + badgesNotifications = legacyData.badges.notifications, + badgesShortcuts = legacyData.badges.shortcuts, + badgesSuspendedApps = legacyData.badges.suspendedApps, + calculatorEnabled = legacyData.calculatorSearch.enabled, + calendarSearchEnabled = legacyData.calendarSearch.enabled, + clockWidgetAlarmPart = legacyData.clockWidget.alarmPart, + clockWidgetBatteryPart = legacyData.clockWidget.batteryPart, + clockWidgetDatePart = legacyData.clockWidget.datePart, + clockWidgetColors = when (legacyData.clockWidget.color) { + LegacySettings.ClockWidgetSettings.ClockWidgetColors.Light -> ClockWidgetColors.Light + LegacySettings.ClockWidgetSettings.ClockWidgetColors.Dark -> ClockWidgetColors.Dark + else -> ClockWidgetColors.Auto + }, + clockWidgetAlignment = when (legacyData.clockWidget.alignment) { + LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Top -> ClockWidgetAlignment.Top + LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Center -> ClockWidgetAlignment.Center + else -> ClockWidgetAlignment.Bottom + }, + homeScreenDock = legacyData.clockWidget.favoritesPart, + clockWidgetFillHeight = legacyData.clockWidget.fillHeight, + clockWidgetCompact = legacyData.clockWidget.layout == LegacySettings.ClockWidgetSettings.ClockWidgetLayout.Horizontal, + clockWidgetMusicPart = legacyData.clockWidget.musicPart, + clockWidgetStyle = when (legacyData.clockWidget.clockStyle) { + LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1 -> ClockWidgetStyle.Digital1() + LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock2 -> ClockWidgetStyle.Digital2 + LegacySettings.ClockWidgetSettings.ClockStyle.OrbitClock -> ClockWidgetStyle.Orbit + LegacySettings.ClockWidgetSettings.ClockStyle.BinaryClock -> ClockWidgetStyle.Binary + LegacySettings.ClockWidgetSettings.ClockStyle.AnalogClock -> ClockWidgetStyle.Analog + LegacySettings.ClockWidgetSettings.ClockStyle.EmptyClock -> ClockWidgetStyle.Empty + LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_MDY -> ClockWidgetStyle.Digital1( + variant = ClockWidgetStyle.Digital1.Variant.MDY + ) + + LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_Outlined -> ClockWidgetStyle.Digital1( + outlined = true + ) + + LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1_OnePlus -> ClockWidgetStyle.Digital1( + variant = ClockWidgetStyle.Digital1.Variant.OnePlus + ) + + else -> ClockWidgetStyle.Digital1() + }, + contactSearchEnabled = legacyData.contactsSearch.enabled, + easterEgg = legacyData.easterEgg, + favoritesEditButton = legacyData.favorites.editButton, + favoritesEnabled = legacyData.favorites.enabled, + favoritesFrequentlyUsed = legacyData.favorites.frequentlyUsed, + favoritesFrequentlyUsedRows = legacyData.favorites.frequentlyUsedRows, + rankingWeightFactor = when (legacyData.resultOrdering.weightFactor) { + LegacySettings.SearchResultOrderingSettings.WeightFactor.Low -> WeightFactor.Low + LegacySettings.SearchResultOrderingSettings.WeightFactor.High -> WeightFactor.High + else -> WeightFactor.Default + }, + fileSearchProviders = buildSet { + if (legacyData.fileSearch.localFiles) add("local") + if (legacyData.fileSearch.nextcloud) add("nextcloud") + if (legacyData.fileSearch.gdrive) add("gdrive") + if (legacyData.fileSearch.onedrive) add("onedrive") + }, + gesturesDoubleTap = makeGestureSettings( + legacyData.gestures.doubleTap, + legacyData.gestures.doubleTapApp + ), + gesturesSwipeDown = makeGestureSettings( + legacyData.gestures.swipeDown, + legacyData.gestures.swipeDownApp + ), + gesturesSwipeLeft = makeGestureSettings( + legacyData.gestures.swipeLeft, + legacyData.gestures.swipeLeftApp + ), + gesturesSwipeRight = makeGestureSettings( + legacyData.gestures.swipeRight, + legacyData.gestures.swipeRightApp + ), + gesturesLongPress = makeGestureSettings( + legacyData.gestures.longPress, + legacyData.gestures.longPressApp + ), + gesturesHomeButton = makeGestureSettings( + legacyData.gestures.homeButton, + legacyData.gestures.homeButtonApp + ), + gridIconSize = legacyData.grid.iconSize, + gridLabels = legacyData.grid.showLabels, + mediaAllowList = legacyData.musicWidget.allowListList.toSet(), + mediaDenyList = legacyData.musicWidget.denyListList.toSet(), + hiddenItemsShowButton = legacyData.searchBar.hiddenItemsButton, + iconsAdaptify = legacyData.icons.adaptify, + iconsForceThemed = legacyData.icons.forceThemed, + iconsPack = legacyData.icons.iconPack.takeIf { it.isNotBlank() }, + iconsPackThemed = legacyData.icons.iconPackThemed, + iconsShape = when (legacyData.icons.shape) { + LegacySettings.IconSettings.IconShape.Circle -> IconShape.Circle + LegacySettings.IconSettings.IconShape.Square -> IconShape.Square + LegacySettings.IconSettings.IconShape.Squircle -> IconShape.Squircle + LegacySettings.IconSettings.IconShape.RoundedSquare -> IconShape.RoundedSquare + LegacySettings.IconSettings.IconShape.EasterEgg -> IconShape.EasterEgg + LegacySettings.IconSettings.IconShape.Hexagon -> IconShape.Hexagon + LegacySettings.IconSettings.IconShape.Triangle -> IconShape.Triangle + LegacySettings.IconSettings.IconShape.Pentagon -> IconShape.Pentagon + LegacySettings.IconSettings.IconShape.Teardrop -> IconShape.Teardrop + LegacySettings.IconSettings.IconShape.Pebble -> IconShape.Pebble + else -> IconShape.PlatformDefault + }, + iconsThemed = legacyData.icons.themedIcons, + searchBarBottom = legacyData.layout.bottomSearchBar, + searchBarColors = when (legacyData.systemBars.statusBarColor) { + LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SearchBarColors.Light + LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SearchBarColors.Dark + else -> SearchBarColors.Auto + }, + searchBarFixed = legacyData.layout.bottomSearchBar, + searchBarKeyboard = legacyData.searchBar.autoFocus, + searchBarStyle = when (legacyData.searchBar.searchBarStyle) { + LegacySettings.SearchBarSettings.SearchBarStyle.Hidden -> SearchBarStyle.Hidden + LegacySettings.SearchBarSettings.SearchBarStyle.Solid -> SearchBarStyle.Solid + else -> SearchBarStyle.Transparent + }, + searchLaunchOnEnter = legacyData.searchBar.launchOnEnter, + searchResultOrder = when (legacyData.resultOrdering.ordering) { + LegacySettings.SearchResultOrderingSettings.Ordering.Alphabetic -> SearchResultOrder.Alphabetical + LegacySettings.SearchResultOrderingSettings.Ordering.LaunchCount -> SearchResultOrder.LaunchCount + else -> SearchResultOrder.Weighted + }, + searchResultsReversed = legacyData.layout.reverseSearchResults, + shortcutSearchEnabled = legacyData.appShortcutSearch.enabled, + stateTagsMultiline = legacyData.ui.searchTagsMultiline, + surfacesBorderWidth = legacyData.cards.borderWidth, + surfacesOpacity = legacyData.cards.opacity, + surfacesRadius = legacyData.cards.radius, + surfacesShape = when (legacyData.cards.shape) { + LegacySettings.CardSettings.Shape.Cut -> SurfaceShape.Cut + else -> SurfaceShape.Rounded + }, + systemBarsHideNav = legacyData.systemBars.hideNavBar, + systemBarsHideStatus = legacyData.systemBars.hideStatusBar, + systemBarsNavColors = when (legacyData.systemBars.navBarColor) { + LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SystemBarColors.Light + LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SystemBarColors.Dark + else -> SystemBarColors.Auto + }, + systemBarsStatusColors = when (legacyData.systemBars.statusBarColor) { + LegacySettings.SystemBarsSettings.SystemBarColors.Light -> SystemBarColors.Light + LegacySettings.SystemBarsSettings.SystemBarColors.Dark -> SystemBarColors.Dark + else -> SystemBarColors.Auto + }, + uiColorScheme = when (legacyData.appearance.theme) { + LegacySettings.AppearanceSettings.Theme.Light -> ColorScheme.Light + LegacySettings.AppearanceSettings.Theme.Dark -> ColorScheme.Dark + else -> ColorScheme.System + }, + uiFont = when (legacyData.appearance.font) { + LegacySettings.AppearanceSettings.Font.SystemDefault -> Font.System + else -> Font.Outfit + }, + uiOrientation = when (legacyData.layout.fixedRotation) { + true -> ScreenOrientation.Portrait + else -> ScreenOrientation.Auto + }, + uiTheme = when (legacyData.appearance.themeId) { + UUID(0L, 0L).toString() -> ThemeDescriptor.Default + UUID(0L, 1L).toString() -> ThemeDescriptor.BlackAndWhite + else -> ThemeDescriptor.Custom(legacyData.appearance.themeId) + }, + unitConverterCurrencies = legacyData.unitConverterSearch.currencies, + unitConverterEnabled = legacyData.unitConverterSearch.enabled, + wallpaperBlur = legacyData.appearance.blurWallpaper, + weatherImperialUnits = legacyData.weather.imperialUnits, + wallpaperBlurRadius = legacyData.appearance.blurWallpaperRadius, + wallpaperDim = legacyData.appearance.dimWallpaper, + weatherProvider = when (legacyData.weather.provider) { + LegacySettings.WeatherSettings.WeatherProvider.MetNo -> "metno" + LegacySettings.WeatherSettings.WeatherProvider.OpenWeatherMap -> "owm" + LegacySettings.WeatherSettings.WeatherProvider.Here -> "here" + LegacySettings.WeatherSettings.WeatherProvider.BrightSky -> "dwd" + else -> "metno" + }, + websiteSearchEnabled = legacyData.websiteSearch.enabled, + widgetsEditButton = legacyData.widgets.editButton, + wikipediaCustomUrl = legacyData.wikipediaSearch.customUrl.takeIf { it.isNotBlank() }, + wikipediaSearchEnabled = legacyData.wikipediaSearch.enabled, + wikipediaSearchImages = legacyData.wikipediaSearch.images, + ) + } + + private fun makeGestureSettings( + gesture: LegacySettings.GestureSettings.GestureAction, + key: String + ): GestureAction { + return when (gesture) { + LegacySettings.GestureSettings.GestureAction.OpenSearch -> GestureAction.Search + LegacySettings.GestureSettings.GestureAction.OpenNotificationDrawer -> GestureAction.Notifications + LegacySettings.GestureSettings.GestureAction.LockScreen -> GestureAction.ScreenLock + LegacySettings.GestureSettings.GestureAction.OpenQuickSettings -> GestureAction.QuickSettings + LegacySettings.GestureSettings.GestureAction.OpenRecents -> GestureAction.Recents + LegacySettings.GestureSettings.GestureAction.OpenPowerDialog -> GestureAction.PowerMenu + LegacySettings.GestureSettings.GestureAction.LaunchApp -> GestureAction.Launch( + key + ) + + else -> GestureAction.NoAction + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_10_11.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_10_11.kt index e9c93e64..9d5ee3f2 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_10_11.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_10_11.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_10_11: VersionedMigration(10, 11) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppearance( builder.appearance.toBuilder() .setCustomColors( diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_11_12.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_11_12.kt index 5f369d91..d9b25ad0 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_11_12.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_11_12.kt @@ -1,12 +1,12 @@ package de.mm20.launcher2.preferences.migrations import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.GestureSettings -import de.mm20.launcher2.preferences.Settings.LayoutSettings +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.LegacySettings.GestureSettings +import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings class Migration_11_12: VersionedMigration(11, 12) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { val oldLayout = builder.appearance.layout when(oldLayout) { LayoutSettings.Layout.Pager -> { diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_12_13.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_12_13.kt index f0364f44..f7e08e26 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_12_13.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_12_13.kt @@ -1,10 +1,10 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.WeightFactor class Migration_12_13: VersionedMigration(12, 13) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder .setClockWidget( builder.clockWidget.toBuilder() diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_13_14.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_13_14.kt index 03bf61d4..d5697ee2 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_13_14.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_13_14.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_13_14 : VersionedMigration(13, 14) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder .setIcons( builder.icons.toBuilder() diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_14_15.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_14_15.kt index 9c570466..bb62496a 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_14_15.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_14_15.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_14_15: VersionedMigration(14, 15) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setSearchBar( builder.searchBar.toBuilder() .setHiddenItemsButton(true) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_15_16.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_15_16.kt index 5d26c1ee..f316500a 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_15_16.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_15_16.kt @@ -1,11 +1,11 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors.Scheme +import de.mm20.launcher2.preferences.LegacySettings +import de.mm20.launcher2.preferences.LegacySettings.AppearanceSettings.CustomColors.Scheme import palettes.TonalPalette class Migration_15_16 : VersionedMigration(15, 16) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppearance( builder.appearance.toBuilder() .setCustomColors( diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_16_17.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_16_17.kt index a76a18b6..dfe9ec06 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_16_17.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_16_17.kt @@ -1,16 +1,16 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings import java.util.UUID class Migration_16_17: VersionedMigration(16, 17) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppearance( builder.appearance.toBuilder() .setThemeId( when(builder.appearance.colorScheme) { - Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L) - Settings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L) + LegacySettings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L) + LegacySettings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L) else -> UUID(0L, 0L) }.toString() ) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_17_18.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_17_18.kt index 34d1997c..0e3e3b87 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_17_18.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_17_18.kt @@ -1,15 +1,15 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_17_18 : VersionedMigration(17, 18) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder .setAppearance(builder.appearance.toBuilder() .setBlurWallpaperRadius(32) ) .setClockWidget(builder.clockWidget.toBuilder() - .setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) + .setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) ) } } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_1_2.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_1_2.kt index ca93bb7a..e048b2d1 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_1_2.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_1_2.kt @@ -1,10 +1,10 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_1_2: VersionedMigration(1, 2) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setSystemBars( builder.systemBars.toBuilder() .setHideNavBar(false) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_2_3.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_2_3.kt index de4d492d..9b2d678d 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_2_3.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_2_3.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_2_3: VersionedMigration(2, 3) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setClockWidget( builder.clockWidget.toBuilder() .setAlarmPart(true) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_3_4.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_3_4.kt index 9cfb4f62..fdd0d14f 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_3_4.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_3_4.kt @@ -1,11 +1,11 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_3_4: VersionedMigration(3, 4) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppShortcutSearch( - Settings.AppShortcutSearchSettings.newBuilder() + LegacySettings.AppShortcutSearchSettings.newBuilder() .setEnabled(true) ) } diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_4_5.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_4_5.kt index 23fa9f64..f758a093 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_4_5.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_4_5.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_4_5: VersionedMigration(4, 5) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setGrid( builder.grid.toBuilder() .setIconSize(48) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt index fa94cf67..9552bfa1 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_5_6.kt @@ -3,13 +3,14 @@ package de.mm20.launcher2.preferences.migrations import de.mm20.launcher2.preferences.DefaultCustomColorsBase import de.mm20.launcher2.preferences.DefaultDarkCustomColorScheme import de.mm20.launcher2.preferences.DefaultLightCustomColorScheme -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_5_6: VersionedMigration(5, 6) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppearance( builder.appearance.toBuilder() - .setCustomColors(Settings.AppearanceSettings.CustomColors.newBuilder() + .setCustomColors( + LegacySettings.AppearanceSettings.CustomColors.newBuilder() .setAdvancedMode(false) .setBaseColors(DefaultCustomColorsBase) .setLightScheme(DefaultLightCustomColorScheme) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_6_7.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_6_7.kt index 136a904d..01591722 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_6_7.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_6_7.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_6_7 : VersionedMigration(6, 7) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setIcons( builder.icons.toBuilder() .setAdaptify(true) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_7_8.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_7_8.kt index 9be5ffdd..dcef3798 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_7_8.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_7_8.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_7_8: VersionedMigration(7, 8) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder.setAppearance( builder.appearance.toBuilder() .setBlurWallpaper(true) diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_8_9.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_8_9.kt index b4eaa47c..7875dad3 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_8_9.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_8_9.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_8_9: VersionedMigration(8, 9) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder .setClockWidget( builder.clockWidget.toBuilder() diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_9_10.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_9_10.kt index 8bafb65c..68b303b7 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_9_10.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/Migration_9_10.kt @@ -1,9 +1,9 @@ package de.mm20.launcher2.preferences.migrations -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings class Migration_9_10 : VersionedMigration(9, 10) { - override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder { + override suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder { return builder .setFavorites( builder.favorites.toBuilder() @@ -12,7 +12,7 @@ class Migration_9_10 : VersionedMigration(9, 10) { .setEditButton(true) ) .setWidgets( - Settings.WidgetSettings.newBuilder() + LegacySettings.WidgetSettings.newBuilder() .setEditButton(true) ) } diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/VersionedMigration.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/VersionedMigration.kt index b1128880..4682d3f4 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/VersionedMigration.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/migrations/VersionedMigration.kt @@ -2,15 +2,15 @@ package de.mm20.launcher2.preferences.migrations import androidx.datastore.core.DataMigration import de.mm20.launcher2.preferences.SchemaVersion -import de.mm20.launcher2.preferences.Settings +import de.mm20.launcher2.preferences.LegacySettings abstract class VersionedMigration( private val fromVersion: Int, private val toVersion: Int -) : DataMigration { - abstract suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder +) : DataMigration { + abstract suspend fun applyMigrations(builder: LegacySettings.Builder): LegacySettings.Builder - final override suspend fun migrate(currentData: Settings): Settings { + final override suspend fun migrate(currentData: LegacySettings): LegacySettings { val builder = currentData.toBuilder() applyMigrations(builder) builder.version = toVersion @@ -19,7 +19,7 @@ abstract class VersionedMigration( override suspend fun cleanUp() {} - override suspend fun shouldMigrate(currentData: Settings): Boolean { + override suspend fun shouldMigrate(currentData: LegacySettings): Boolean { return currentData.version <= fromVersion && SchemaVersion >= toVersion } } \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalculatorSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalculatorSearchSettings.kt new file mode 100644 index 00000000..c2066f70 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalculatorSearchSettings.kt @@ -0,0 +1,18 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class CalculatorSearchSettings internal constructor( + private val dataStore: LauncherDataStore, +){ + val enabled + get() = dataStore.data.map { it.calculatorEnabled }.distinctUntilChanged() + + fun setEnabled(enabled: Boolean) { + dataStore.update { + it.copy(calculatorEnabled = enabled) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalendarSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalendarSearchSettings.kt new file mode 100644 index 00000000..c41794f1 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/CalendarSearchSettings.kt @@ -0,0 +1,17 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.map + +class CalendarSearchSettings internal constructor( + private val dataStore: LauncherDataStore, +){ + val enabled + get() = dataStore.data.map { it.calendarSearchEnabled } + + fun setEnabled(enabled: Boolean) { + dataStore.update { + it.copy(calendarSearchEnabled = enabled) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ContactSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ContactSearchSettings.kt new file mode 100644 index 00000000..0565e064 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ContactSearchSettings.kt @@ -0,0 +1,15 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class ContactSearchSettings internal constructor(private val dataStore: LauncherDataStore) { + val enabled: Flow + get() = dataStore.data.map { it.contactSearchEnabled }.distinctUntilChanged() + + fun setEnabled(enabled: Boolean) { + dataStore.update { it.copy(contactSearchEnabled = enabled) } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FavoritesSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FavoritesSettings.kt new file mode 100644 index 00000000..593f528f --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FavoritesSettings.kt @@ -0,0 +1,44 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class FavoritesSettingsData( + val columns: Int, + val frequentlyUsed: Boolean, + val frequentlyUsedRows: Int, +) + +class FavoritesSettings internal constructor( + private val dataStore: LauncherDataStore, +) : Flow by (dataStore.data.map { + FavoritesSettingsData( + columns = it.favoritesFrequentlyUsedRows, + frequentlyUsed = it.favoritesFrequentlyUsed, + frequentlyUsedRows = it.favoritesFrequentlyUsedRows, + ) +}.distinctUntilChanged()) { + + val showEditButton + get() = dataStore.data.map { it.favoritesEditButton }.distinctUntilChanged() + + fun setShowEditButton(showEditButton: Boolean) { + dataStore.update { it.copy(favoritesEditButton = showEditButton) } + } + + val frequentlyUsed: Flow + get() = dataStore.data.map { it.favoritesFrequentlyUsed }.distinctUntilChanged() + + fun setFrequentlyUsed(frequentlyUsed: Boolean) { + dataStore.update { it.copy(favoritesFrequentlyUsed = frequentlyUsed) } + } + + val frequentlyUsedRows: Flow + get() = dataStore.data.map { it.favoritesFrequentlyUsedRows }.distinctUntilChanged() + + fun setFrequentlyUsedRows(frequentlyUsedRows: Int) { + dataStore.update { it.copy(favoritesFrequentlyUsedRows = frequentlyUsedRows) } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FileSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FileSearchSettings.kt new file mode 100644 index 00000000..fccf53bf --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/FileSearchSettings.kt @@ -0,0 +1,77 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class FileSearchSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) { + val enabledProviders: Flow> + get() = launcherDataStore.data.map { it.fileSearchProviders } + + val localFiles + get() = launcherDataStore.data.map { it.fileSearchProviders.contains("local") } + + fun setLocalFiles(localFiles: Boolean) { + launcherDataStore.update { + if (localFiles) { + it.copy(fileSearchProviders = it.fileSearchProviders + "local") + } else { + it.copy(fileSearchProviders = it.fileSearchProviders - "local") + } + } + } + + val gdriveFiles + get() = launcherDataStore.data.map { it.fileSearchProviders.contains("gdrive") } + + fun setGdriveFiles(gdriveFiles: Boolean) { + launcherDataStore.update { + if (gdriveFiles) { + it.copy(fileSearchProviders = it.fileSearchProviders + "gdrive") + } else { + it.copy(fileSearchProviders = it.fileSearchProviders - "gdrive") + } + } + } + + val nextcloudFiles + get() = launcherDataStore.data.map { it.fileSearchProviders.contains("nextcloud") } + + fun setNextcloudFiles(nextcloudFiles: Boolean) { + launcherDataStore.update { + if (nextcloudFiles) { + it.copy(fileSearchProviders = it.fileSearchProviders + "nextcloud") + } else { + it.copy(fileSearchProviders = it.fileSearchProviders - "nextcloud") + } + } + } + + val owncloudFiles + get() = launcherDataStore.data.map { it.fileSearchProviders.contains("owncloud") } + + fun setOwncloudFiles(owncloudFiles: Boolean) { + launcherDataStore.update { + if (owncloudFiles) { + it.copy(fileSearchProviders = it.fileSearchProviders + "owncloud") + } else { + it.copy(fileSearchProviders = it.fileSearchProviders - "owncloud") + } + } + } + + val enabledPlugins: Flow> + get() = launcherDataStore.data.map { it.fileSearchProviders - "local" - "gdrive" - "nextcloud" - "owncloud" } + + fun setPluginEnabled(authority: String, enabled: Boolean) { + launcherDataStore.update { + if (enabled) { + it.copy(fileSearchProviders = it.fileSearchProviders + authority) + } else { + it.copy(fileSearchProviders = it.fileSearchProviders - authority) + } + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/RankingSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/RankingSettings.kt new file mode 100644 index 00000000..8dd24d03 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/RankingSettings.kt @@ -0,0 +1,19 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.WeightFactor +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class RankingSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +){ + val weightFactor + get() = launcherDataStore.data.map { it.rankingWeightFactor }.distinctUntilChanged() + + fun setWeightFactor(weightFactor: WeightFactor) { + launcherDataStore.update { + it.copy(rankingWeightFactor = weightFactor) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ShortcutSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ShortcutSearchSettings.kt new file mode 100644 index 00000000..044da505 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/ShortcutSearchSettings.kt @@ -0,0 +1,18 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class ShortcutSearchSettings internal constructor( + private val dataStore: LauncherDataStore +) { + val enabled + get() = dataStore.data.map { it.shortcutSearchEnabled }.distinctUntilChanged() + + fun setEnabled(enabled: Boolean) { + dataStore.update { + it.copy(shortcutSearchEnabled = enabled) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/UnitConverterSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/UnitConverterSettings.kt new file mode 100644 index 00000000..521d9199 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/UnitConverterSettings.kt @@ -0,0 +1,34 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class UnitConverterSettingsData( + val enabled: Boolean, + val currencies: Boolean, +) + +class UnitConverterSettings internal constructor( + private val dataStore: LauncherDataStore, +) : Flow by (dataStore.data.map { + UnitConverterSettingsData( + enabled = it.unitConverterEnabled, + currencies = it.unitConverterCurrencies, + ) +}.distinctUntilChanged()) { + val enabled: Flow + get() = dataStore.data.map { it.unitConverterEnabled }.distinctUntilChanged() + + fun setEnabled(enabled: Boolean) { + dataStore.update { it.copy(unitConverterEnabled = enabled) } + } + + val currencies: Flow + get() = dataStore.data.map { it.unitConverterCurrencies }.distinctUntilChanged() + + fun setCurrencies(currencies: Boolean) { + dataStore.update { it.copy(unitConverterCurrencies = currencies) } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WebsiteSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WebsiteSearchSettings.kt new file mode 100644 index 00000000..93ea450f --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WebsiteSearchSettings.kt @@ -0,0 +1,17 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.map + +class WebsiteSearchSettings internal constructor( + private val dataStore: LauncherDataStore, +){ + val enabled + get() = dataStore.data.map { it.websiteSearchEnabled } + + fun setEnabled(enabled: Boolean) { + dataStore.update { + it.copy(websiteSearchEnabled = enabled) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WikipediaSearchSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WikipediaSearchSettings.kt new file mode 100644 index 00000000..00b7f7cc --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WikipediaSearchSettings.kt @@ -0,0 +1,27 @@ +package de.mm20.launcher2.preferences.search + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class WikipediaSearchSettings internal constructor( + private val dataStore: LauncherDataStore +) { + val enabled + get() = dataStore.data.map { it.wikipediaSearchEnabled }.distinctUntilChanged() + + fun setEnabled(enabled: Boolean) { + dataStore.update { + it.copy(wikipediaSearchEnabled = enabled) + } + } + + val customUrl + get() = dataStore.data.map { it.wikipediaCustomUrl }.distinctUntilChanged() + + fun setCustomUrl(customUrl: String?) { + dataStore.update { + it.copy(wikipediaCustomUrl = customUrl?.takeIf { it.isNotBlank() }) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/BadgeSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/BadgeSettings.kt new file mode 100644 index 00000000..a5f35e50 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/BadgeSettings.kt @@ -0,0 +1,71 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +data class BadgeSettingsData( + val notifications: Boolean = true, + val suspendedApps: Boolean = true, + val cloudFiles: Boolean = true, + val shortcuts: Boolean = true, + val plugins: Boolean = true, +) + +class BadgeSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) : Flow by (launcherDataStore.data.map { + BadgeSettingsData( + notifications = it.badgesNotifications, + suspendedApps = it.badgesSuspendedApps, + cloudFiles = it.badgesCloudFiles, + shortcuts = it.badgesShortcuts, + plugins = it.badgesPlugins, + ) +}) { + + val notifications + get() = launcherDataStore.data.map { it.badgesNotifications } + + fun setNotifications(notifications: Boolean) { + launcherDataStore.update { + it.copy(badgesNotifications = notifications) + } + } + + val suspendedApps + get() = launcherDataStore.data.map { it.badgesSuspendedApps } + + fun setSuspendedApps(suspendedApps: Boolean) { + launcherDataStore.update { + it.copy(badgesSuspendedApps = suspendedApps) + } + } + + val cloudFiles + get() = launcherDataStore.data.map { it.badgesCloudFiles } + + fun setCloudFiles(cloudFiles: Boolean) { + launcherDataStore.update { + it.copy(badgesCloudFiles = cloudFiles) + } + } + + val shortcuts + get() = launcherDataStore.data.map { it.badgesShortcuts } + + fun setShortcuts(shortcuts: Boolean) { + launcherDataStore.update { + it.copy(badgesShortcuts = shortcuts) + } + } + + val plugins + get() = launcherDataStore.data.map { it.badgesPlugins } + + fun setPlugins(plugins: Boolean) { + launcherDataStore.update { + it.copy(badgesPlugins = plugins) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt new file mode 100644 index 00000000..34f73bca --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/ClockWidgetSettings.kt @@ -0,0 +1,107 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.ClockWidgetAlignment +import de.mm20.launcher2.preferences.ClockWidgetColors +import de.mm20.launcher2.preferences.ClockWidgetStyle +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class ClockWidgetParts( + val date: Boolean, + val music: Boolean = false, + val battery: Boolean = false, + val alarm: Boolean = false, +) + +class ClockWidgetSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) { + val compact + get() = launcherDataStore.data.map { it.clockWidgetCompact } + + fun setCompact(compact: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetCompact = compact) + } + } + + val parts + get() = launcherDataStore.data.map { + ClockWidgetParts( + date = it.clockWidgetDatePart, + music = it.clockWidgetMusicPart, + battery = it.clockWidgetBatteryPart, + alarm = it.clockWidgetAlarmPart, + ) + }.distinctUntilChanged() + + fun setDatePart(datePart: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetDatePart = datePart) + } + } + + fun setMusicPart(musicPart: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetMusicPart = musicPart) + } + } + + fun setBatteryPart(batteryPart: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetBatteryPart = batteryPart) + } + } + + fun setAlarmPart(alarmPart: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetAlarmPart = alarmPart) + } + } + + val fillHeight + get() = launcherDataStore.data.map { it.clockWidgetFillHeight } + + fun setFillHeight(fillHeight: Boolean) { + launcherDataStore.update { + it.copy(clockWidgetFillHeight = fillHeight) + } + } + + val dock + get() = launcherDataStore.data.map { it.homeScreenDock } + + fun setDock(dock: Boolean) { + launcherDataStore.update { + it.copy(homeScreenDock = dock) + } + } + + val alignment + get() = launcherDataStore.data.map { it.clockWidgetAlignment } + + fun setAlignment(alignment: ClockWidgetAlignment) { + launcherDataStore.update { + it.copy(clockWidgetAlignment = alignment) + } + } + + val clockStyle + get() = launcherDataStore.data.map { it.clockWidgetStyle } + + fun setClockStyle(clockStyle: ClockWidgetStyle) { + launcherDataStore.update { + it.copy(clockWidgetStyle = clockStyle) + } + } + + fun setColor(color: ClockWidgetColors) { + launcherDataStore.update { + it.copy(clockWidgetColors = color) + } + } + + val color + get() = launcherDataStore.data.map { it.clockWidgetColors } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/GestureSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/GestureSettings.kt new file mode 100644 index 00000000..b8f2b6c6 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/GestureSettings.kt @@ -0,0 +1,87 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.GestureAction +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class GestureSettingsData( + val swipeDown: GestureAction, + val swipeLeft: GestureAction, + val swipeRight: GestureAction, + val doubleTap: GestureAction, + val longPress: GestureAction, + val homeButton: GestureAction, +) + +class GestureSettings internal constructor( + private val dataStore: LauncherDataStore, +): Flow by ( + dataStore.data.map { + GestureSettingsData( + swipeDown = it.gesturesSwipeDown, + swipeLeft = it.gesturesSwipeLeft, + swipeRight = it.gesturesSwipeRight, + doubleTap = it.gesturesDoubleTap, + longPress = it.gesturesLongPress, + homeButton = it.gesturesHomeButton, + ) + }.distinctUntilChanged() +) { + val swipeDown: Flow = dataStore.data.map { it.gesturesSwipeDown } + .distinctUntilChanged() + + val swipeLeft: Flow = dataStore.data.map { it.gesturesSwipeLeft } + .distinctUntilChanged() + + val swipeRight: Flow = dataStore.data.map { it.gesturesSwipeRight } + .distinctUntilChanged() + + val doubleTap: Flow = dataStore.data.map { it.gesturesDoubleTap } + .distinctUntilChanged() + + val longPress: Flow = dataStore.data.map { it.gesturesLongPress } + .distinctUntilChanged() + + val homeButton: Flow = dataStore.data.map { it.gesturesHomeButton } + .distinctUntilChanged() + + fun setSwipeDown(action: GestureAction) { + dataStore.update { + it.copy(gesturesSwipeDown = action) + } + } + + fun setSwipeLeft(action: GestureAction) { + dataStore.update { + it.copy(gesturesSwipeLeft = action) + } + } + + fun setSwipeRight(action: GestureAction) { + dataStore.update { + it.copy(gesturesSwipeRight = action) + } + } + + fun setDoubleTap(action: GestureAction) { + dataStore.update { + it.copy(gesturesDoubleTap = action) + } + } + + fun setLongPress(action: GestureAction) { + dataStore.update { + it.copy(gesturesLongPress = action) + } + } + + fun setHomeButton(action: GestureAction) { + dataStore.update { + it.copy(gesturesHomeButton = action) + } + } + + +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/IconSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/IconSettings.kt new file mode 100644 index 00000000..582439ac --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/IconSettings.kt @@ -0,0 +1,62 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class IconSettingsData( + val themedIcons: Boolean, + val forceThemed: Boolean, + val adaptify: Boolean, + val iconPack: String?, + val iconPackThemed: Boolean, +) + +class IconSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) : Flow by ( + launcherDataStore.data.map { + IconSettingsData( + themedIcons = it.iconsThemed, + forceThemed = it.iconsForceThemed, + adaptify = it.iconsAdaptify, + iconPack = it.iconsPack, + iconPackThemed = it.iconsPackThemed, + ) + } + ) { + + fun setAdaptifyLegacyIcons(adaptify: Boolean) { + launcherDataStore.update { + it.copy(iconsAdaptify = adaptify) + } + } + + fun setThemedIcons(themedIcons: Boolean) { + launcherDataStore.update { + it.copy(iconsThemed = themedIcons) + } + } + + fun setForceThemedIcons(forceThemed: Boolean) { + launcherDataStore.update { + it.copy(iconsForceThemed = forceThemed) + } + } + + fun setIconPack(iconPack: String?) { + launcherDataStore.update { + it.copy(iconsPack = iconPack) + } + } + + fun setIconPackThemed(iconPackThemed: Boolean) { + launcherDataStore.update { + it.copy(iconsPackThemed = iconPackThemed) + } + } + + + +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/SearchUiSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/SearchUiSettings.kt new file mode 100644 index 00000000..3f869f44 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/SearchUiSettings.kt @@ -0,0 +1,65 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.SearchResultOrder +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class SearchUiSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +){ + val launchOnEnter + get() = launcherDataStore.data.map { it.searchLaunchOnEnter }.distinctUntilChanged() + + fun setLaunchOnEnter(launchOnEnter: Boolean) { + launcherDataStore.update { + it.copy(searchLaunchOnEnter = launchOnEnter) + } + } + + val hiddenItemsButton + get() = launcherDataStore.data.map { it.hiddenItemsShowButton }.distinctUntilChanged() + + fun setHiddenItemsButton(hiddenItemsButton: Boolean) { + launcherDataStore.update { + it.copy(hiddenItemsShowButton = hiddenItemsButton) + } + } + + val favorites + get() = launcherDataStore.data.map { it.favoritesEnabled }.distinctUntilChanged() + + fun setFavorites(favorites: Boolean) { + launcherDataStore.update { + it.copy(favoritesEnabled = favorites) + } + } + + val resultOrder + get() = launcherDataStore.data.map { it.searchResultOrder }.distinctUntilChanged() + + fun setResultOrder(resultOrder: SearchResultOrder) { + launcherDataStore.update { + it.copy(searchResultOrder = resultOrder) + } + } + + val openKeyboard + get() = launcherDataStore.data.map { it.searchBarKeyboard }.distinctUntilChanged() + + fun setOpenKeyboard(openKeyboard: Boolean) { + launcherDataStore.update { + it.copy(searchBarKeyboard = openKeyboard) + } + } + + val reversedResults + get() = launcherDataStore.data.map { it.searchResultsReversed }.distinctUntilChanged() + + fun setReversedResults(reversedResults: Boolean) { + launcherDataStore.update { + it.copy(searchResultsReversed = reversedResults) + } + } + +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt new file mode 100644 index 00000000..76707e15 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt @@ -0,0 +1,334 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.BaseLayout +import de.mm20.launcher2.preferences.ColorScheme +import de.mm20.launcher2.preferences.Font +import de.mm20.launcher2.preferences.IconShape +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ScreenOrientation +import de.mm20.launcher2.preferences.SearchBarColors +import de.mm20.launcher2.preferences.SearchBarStyle +import de.mm20.launcher2.preferences.SurfaceShape +import de.mm20.launcher2.preferences.SystemBarColors +import de.mm20.launcher2.preferences.ThemeDescriptor +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +data class CardStyle( + val opacity: Float = 1f, + val cornerRadius: Int = 0, + val shape: SurfaceShape = SurfaceShape.Rounded, + val borderWidth: Int = 0, +) + +data class GridSettings( + val columnCount: Int = 5, + val iconSize: Int = 48, + val showLabels: Boolean = true, +) + +class UiSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) { + val favoritesEnabled + get() = launcherDataStore.data.map { it.favoritesEnabled || it.homeScreenDock } + + val iconShape + get() = launcherDataStore.data.map { + it.iconsShape + } + + fun setIconShape(iconShape: IconShape) { + launcherDataStore.update { + it.copy(iconsShape = iconShape) + } + } + + val gridSettings + get() = launcherDataStore.data.map { + GridSettings( + showLabels = it.gridLabels, + iconSize = it.gridIconSize, + columnCount = it.gridColumnCount, + ) + } + + fun setGridColumnCount(columnCount: Int) { + launcherDataStore.update { + it.copy(gridColumnCount = columnCount) + } + } + + fun setGridIconSize(iconSize: Int) { + launcherDataStore.update { + it.copy(gridIconSize = iconSize) + } + } + + fun setGridShowLabels(showLabels: Boolean) { + launcherDataStore.update { + it.copy(gridLabels = showLabels) + } + } + + + val cardStyle + get() = launcherDataStore.data.map { + CardStyle( + opacity = it.surfacesOpacity, + cornerRadius = it.surfacesRadius, + shape = it.surfacesShape, + borderWidth = it.surfacesBorderWidth, + ) + } + + fun setCardOpacity(opacity: Float) { + launcherDataStore.update { + it.copy(surfacesOpacity = opacity) + } + } + + fun setCardRadius(radius: Int) { + launcherDataStore.update { + it.copy(surfacesRadius = radius) + } + } + + fun setCardBorderWidth(borderWidth: Int) { + launcherDataStore.update { + it.copy(surfacesBorderWidth = borderWidth) + } + } + + fun setCardShape(shape: SurfaceShape) { + launcherDataStore.update { + it.copy(surfacesShape = shape) + } + } + + val dimWallpaper + get() = launcherDataStore.data.map { + it.wallpaperDim + } + + fun setDimWallpaper(dimWallpaper: Boolean) { + launcherDataStore.update { + it.copy(wallpaperDim = dimWallpaper) + } + } + + val blurWallpaper + get() = launcherDataStore.data.map { + it.wallpaperBlur + }.distinctUntilChanged() + + fun setBlurWallpaper(blurWallpaper: Boolean) { + launcherDataStore.update { + it.copy(wallpaperBlur = blurWallpaper) + } + } + + val wallpaperBlurRadius + get() = launcherDataStore.data.map { + it.wallpaperBlurRadius + }.distinctUntilChanged() + + fun setWallpaperBlurRadius(wallpaperBlurRadius: Int) { + launcherDataStore.update { + it.copy(wallpaperBlurRadius = wallpaperBlurRadius) + } + } + + val colorScheme + get() = launcherDataStore.data.map { + it.uiColorScheme + }.distinctUntilChanged() + + val statusBarColor + get() = launcherDataStore.data.map { + it.systemBarsStatusColors + }.distinctUntilChanged() + + val hideStatusBar + get() = launcherDataStore.data.map { + it.systemBarsHideStatus + }.distinctUntilChanged() + + val hideNavigationBar + get() = launcherDataStore.data.map { + it.systemBarsHideNav + }.distinctUntilChanged() + + fun setHideStatusBar(hideStatusBar: Boolean) { + launcherDataStore.update { + it.copy(systemBarsHideStatus = hideStatusBar) + } + } + + fun setHideNavigationBar(hideNavigationBar: Boolean) { + launcherDataStore.update { + it.copy(systemBarsHideNav = hideNavigationBar) + } + } + + val navigationBarColor + get() = launcherDataStore.data.map { + it.systemBarsNavColors + }.distinctUntilChanged() + + fun setStatusBarColor(statusBarColor: SystemBarColors) { + launcherDataStore.update { + it.copy(systemBarsStatusColors = statusBarColor) + } + } + + fun setNavigationBarColor(navigationBarColor: SystemBarColors) { + launcherDataStore.update { + it.copy(systemBarsNavColors = navigationBarColor) + } + } + + val chargingAnimation + get() = launcherDataStore.data.map { + it.animationsCharging + }.distinctUntilChanged() + + fun setChargingAnimation(chargingAnimation: Boolean) { + launcherDataStore.update { + it.copy(animationsCharging = chargingAnimation) + } + } + + val baseLayout + get() = launcherDataStore.data.map { + it.uiBaseLayout + }.distinctUntilChanged() + + fun setBaseLayout(baseLayout: BaseLayout) { + launcherDataStore.update { + it.copy(uiBaseLayout = baseLayout) + } + } + + val clockFillScreen + get() = launcherDataStore.data.map { + it.clockWidgetFillHeight + }.distinctUntilChanged() + + val searchBarStyle + get() = launcherDataStore.data.map { + it.searchBarStyle + }.distinctUntilChanged() + + fun setSearchBarStyle(searchBarStyle: SearchBarStyle) { + launcherDataStore.update { + it.copy(searchBarStyle = searchBarStyle) + } + } + + val searchBarColor + get() = launcherDataStore.data.map { + it.searchBarColors + }.distinctUntilChanged() + + fun setSearchBarColor(color: SearchBarColors) { + launcherDataStore.update { + it.copy(searchBarColors = color) + } + } + + val bottomSearchBar + get() = launcherDataStore.data.map { + it.searchBarBottom + }.distinctUntilChanged() + + fun setBottomSearchBar(bottomSearchBar: Boolean) { + launcherDataStore.update { + it.copy(searchBarBottom = bottomSearchBar) + } + } + + val reverseSearchResults + get() = launcherDataStore.data.map { + it.searchResultsReversed + }.distinctUntilChanged() + + fun setReverseSearchResults(reverseSearchResults: Boolean) { + launcherDataStore.update { + it.copy(searchResultsReversed = reverseSearchResults) + } + } + + val fixedSearchBar + get() = launcherDataStore.data.map { + it.searchBarFixed + }.distinctUntilChanged() + + fun setFixedSearchBar(fixedSearchBar: Boolean) { + launcherDataStore.update { + it.copy(searchBarFixed = fixedSearchBar) + } + } + + val openKeyboardOnSearch + get() = launcherDataStore.data.map { + it.searchBarKeyboard + }.distinctUntilChanged() + + + val orientation + get() = launcherDataStore.data.map { + it.uiOrientation + }.distinctUntilChanged() + + fun setOrientation(orientation: ScreenOrientation) { + launcherDataStore.update { + it.copy(uiOrientation = orientation) + } + } + + + val theme + get() = launcherDataStore.data.map { + it.uiTheme + }.distinctUntilChanged() + + fun setTheme(theme: ThemeDescriptor) { + launcherDataStore.update { + it.copy(uiTheme = theme) + } + } + + val font + get() = launcherDataStore.data.map { + it.uiFont + }.distinctUntilChanged() + + fun setFont(font: Font) { + launcherDataStore.update { + it.copy(uiFont = font) + } + } + + fun setColorScheme(colorScheme: ColorScheme) { + launcherDataStore.update { + it.copy(uiColorScheme = colorScheme) + } + } + + val dock + get() = launcherDataStore.data.map { + it.homeScreenDock + }.distinctUntilChanged() + + val widgetEditButton + get() = launcherDataStore.data.map { + it.widgetsEditButton + }.distinctUntilChanged() + + fun setWidgetEditButton(editButton: Boolean) { + launcherDataStore.update { + it.copy(widgetsEditButton = editButton) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiState.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiState.kt new file mode 100644 index 00000000..0ab7d929 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiState.kt @@ -0,0 +1,18 @@ +package de.mm20.launcher2.preferences.ui + +import de.mm20.launcher2.preferences.LauncherDataStore +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +class UiState internal constructor( + private val launcherDataStore: LauncherDataStore, +){ + val favoritesTagsExpanded + get() = launcherDataStore.data.map { it.stateTagsMultiline }.distinctUntilChanged() + + fun setFavoritesTagsExpanded(favoritesTagsExpanded: Boolean) { + launcherDataStore.update { + it.copy(stateTagsMultiline = favoritesTagsExpanded) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/weather/WeatherSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/weather/WeatherSettings.kt new file mode 100644 index 00000000..91b4ce18 --- /dev/null +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/weather/WeatherSettings.kt @@ -0,0 +1,165 @@ +package de.mm20.launcher2.preferences.weather + +import de.mm20.launcher2.preferences.LatLon +import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ProviderSettings +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +sealed interface WeatherLocation { + val name: String + + data class LatLon( + override val name: String, + val lat: Double, + val lon: Double, + ) : WeatherLocation + + data class Id( + override val name: String, + val locationId: String, + ) : WeatherLocation +} + +data class WeatherSettingsData( + val provider: String = "metno", + val autoLocation: Boolean = true, + val location: LatLon? = null, + val locationName: String? = null, + val lastLocation: LatLon? = null, + val lastUpdate: Long = 0L, + val providerSettings: Map = emptyMap(), +) + +class WeatherSettings internal constructor( + private val launcherDataStore: LauncherDataStore, +) : + Flow by ( + launcherDataStore.data.map { + WeatherSettingsData( + provider = it.weatherProvider, + autoLocation = it.weatherAutoLocation, + location = it.weatherLocation, + locationName = it.weatherLocationName, + lastLocation = it.weatherLastLocation, + lastUpdate = it.weatherLastUpdate, + providerSettings = it.weatherProviderSettings, + ) + }.distinctUntilChanged() + ) { + + val location = launcherDataStore.data.map { + val providerSettings = it.weatherProviderSettings[it.weatherProvider] + val id = providerSettings?.locationId + val name = providerSettings?.locationName + + if (id != null && name != null) { + WeatherLocation.Id(name, id) + } else if (it.weatherLocation != null && it.weatherLocationName != null) { + WeatherLocation.LatLon( + it.weatherLocationName, + it.weatherLocation.lat, + it.weatherLocation.lon + ) + } else { + null + } + }.distinctUntilChanged() + + val autoLocation = launcherDataStore.data.map { it.weatherAutoLocation } + .distinctUntilChanged() + + fun setLocation(location: WeatherLocation) { + launcherDataStore.update { + val providerSettings = + it.weatherProviderSettings.getOrDefault(it.weatherProvider, ProviderSettings()) + when (location) { + is WeatherLocation.LatLon -> { + it.copy( + weatherLocation = LatLon(lat = location.lat, lon = location.lon), + weatherLocationName = location.name, + weatherLastUpdate = 0L, + weatherAutoLocation = false, + weatherProviderSettings = it.weatherProviderSettings.toMutableMap().apply { + put( + it.weatherProvider, + providerSettings.copy( + locationId = null, + locationName = null, + ) + ) + } + ) + } + + is WeatherLocation.Id -> { + it.copy( + weatherLocation = null, + weatherLocationName = null, + weatherAutoLocation = false, + weatherLastUpdate = 0L, + weatherProviderSettings = it.weatherProviderSettings.toMutableMap().apply { + put( + it.weatherProvider, + providerSettings.copy( + locationId = location.locationId, + locationName = location.name + ) + ) + } + ) + } + } + } + } + + fun setLastLocation(location: LatLon) { + launcherDataStore.update { + it.copy( + weatherLastLocation = location, + ) + } + } + + val lastUpdate = launcherDataStore.data.map { it.weatherLastUpdate } + .distinctUntilChanged() + + fun setLastUpdate(lastUpdate: Long) { + launcherDataStore.update { + it.copy(weatherLastUpdate = lastUpdate) + } + } + + val providerId = launcherDataStore.data.map { it.weatherProvider } + .distinctUntilChanged() + + fun setProvider(provider: String) { + launcherDataStore.update { + it.copy( + weatherProvider = provider, + weatherLastUpdate = 0L, + ) + } + } + + fun setAutoLocation(autoLocation: Boolean) { + launcherDataStore.update { + it.copy( + weatherAutoLocation = autoLocation, + weatherLastUpdate = 0L, + ) + } + } + + val imperialUnits = launcherDataStore.data.map { it.weatherImperialUnits } + .distinctUntilChanged() + + fun setImperialUnits(imperialUnits: Boolean) { + launcherDataStore.update { + it.copy( + weatherImperialUnits = imperialUnits, + ) + } + } +} \ No newline at end of file diff --git a/core/preferences/src/main/proto/settings.proto b/core/preferences/src/main/proto/settings.proto index f57fc567..391c1528 100644 --- a/core/preferences/src/main/proto/settings.proto +++ b/core/preferences/src/main/proto/settings.proto @@ -3,7 +3,7 @@ syntax = "proto3"; option java_package = "de.mm20.launcher2.preferences"; option java_multiple_files = true; -message Settings { +message LegacySettings { uint32 version = 1; message AppearanceSettings { enum Theme { diff --git a/data/appshortcuts/build.gradle.kts b/data/appshortcuts/build.gradle.kts index 28f8709a..1c2e6c3a 100644 --- a/data/appshortcuts/build.gradle.kts +++ b/data/appshortcuts/build.gradle.kts @@ -47,5 +47,6 @@ dependencies { implementation(project(":core:base")) implementation(project(":core:ktx")) implementation(project(":core:crashreporter")) + implementation(project(":core:preferences")) } \ No newline at end of file diff --git a/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/AppShortcutRepository.kt b/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/AppShortcutRepository.kt index cc08084a..fd12e104 100644 --- a/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/AppShortcutRepository.kt +++ b/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/AppShortcutRepository.kt @@ -12,6 +12,7 @@ import androidx.core.content.getSystemService import de.mm20.launcher2.ktx.normalize import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.preferences.search.ShortcutSearchSettings import de.mm20.launcher2.search.AppShortcut import de.mm20.launcher2.search.SearchableRepository import kotlinx.collections.immutable.ImmutableList @@ -24,9 +25,10 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.withContext import org.apache.commons.text.similarity.FuzzyScore @@ -50,6 +52,7 @@ interface AppShortcutRepository : SearchableRepository { internal class AppShortcutRepositoryImpl( private val context: Context, private val permissionsManager: PermissionsManager, + private val settings: ShortcutSearchSettings, ) : AppShortcutRepository { private val scope = CoroutineScope(Dispatchers.Default + Job()) @@ -108,55 +111,57 @@ internal class AppShortcutRepositoryImpl( return flags } - override fun search(query: String) = channelFlow> { + override fun search(query: String): Flow> { if (query.length < 3) { - send(persistentListOf()) - return@channelFlow + return flowOf(persistentListOf()) } - withContext(Dispatchers.IO) { - if (!permissionsManager.checkPermissionOnce(PermissionGroup.AppShortcuts)) { - send(persistentListOf()) - return@withContext - } - shortcutChangeEmitter.collectLatest { - val launcherApps = - context.getSystemService() ?: return@collectLatest send( - persistentListOf() + return combine( + listOf( + settings.enabled, + permissionsManager.hasPermission(PermissionGroup.AppShortcuts), + shortcutChangeEmitter + ) + ) { it } + .map { (enabled, perm, _) -> + enabled as Boolean + perm as Boolean + + if (enabled && perm) { + val launcherApps = + context.getSystemService() ?: return@map persistentListOf() + + + val shortcutQuery = LauncherApps.ShortcutQuery() + shortcutQuery.setQueryFlags( + LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED or + LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or + LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or + LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED or + LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER ) + val shortcuts = launcherApps.getShortcuts(shortcutQuery, Process.myUserHandle()) + ?.filter { + if (it.longLabel != null) { + return@filter matches(it.longLabel.toString(), query) + } + if (it.shortLabel != null) { + return@filter matches(it.shortLabel.toString(), query) + } + return@filter false + } ?: emptyList() - val shortcutQuery = LauncherApps.ShortcutQuery() - shortcutQuery.setQueryFlags( - LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED or - LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC or - LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST or - LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED or - LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER - ) - val shortcuts = launcherApps.getShortcuts(shortcutQuery, Process.myUserHandle()) - ?.filter { - if (it.longLabel != null) { - return@filter matches(it.longLabel.toString(), query) - } - if (it.shortLabel != null) { - return@filter matches(it.shortLabel.toString(), query) - } - return@filter false - } ?: emptyList() - - val pm = context.packageManager - - - send( shortcuts.mapNotNull { LauncherShortcut( context, it ) }.toImmutableList() - ) + + } else { + persistentListOf() + } } - } } private val shortcutChangeEmitter = callbackFlow { diff --git a/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/Module.kt b/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/Module.kt index 2a1f7b26..67b7bead 100644 --- a/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/Module.kt +++ b/data/appshortcuts/src/main/java/de/mm20/launcher2/appshortcuts/Module.kt @@ -8,8 +8,8 @@ import org.koin.core.qualifier.named import org.koin.dsl.module val appShortcutsModule = module { - factory>(named()) { AppShortcutRepositoryImpl(androidContext(), get()) } - factory { AppShortcutRepositoryImpl(androidContext(), get()) } + factory { AppShortcutRepositoryImpl(androidContext(), get(), get()) } + factory>(named()) { get() } factory(named(LauncherShortcut.Domain)) { LauncherShortcutDeserializer(androidContext()) } factory(named(LegacyShortcut.Domain)) { LegacyShortcutDeserializer(androidContext()) } } \ No newline at end of file diff --git a/data/calculator/build.gradle.kts b/data/calculator/build.gradle.kts index 853b419f..44c351e4 100644 --- a/data/calculator/build.gradle.kts +++ b/data/calculator/build.gradle.kts @@ -45,5 +45,6 @@ dependencies { implementation(libs.koin.android) implementation(project(":core:base")) + implementation(project(":core:preferences")) } \ No newline at end of file diff --git a/data/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt b/data/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt index 04ad47f7..b3a8b083 100644 --- a/data/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt +++ b/data/calculator/src/main/java/de/mm20/launcher2/calculator/CalculatorRepository.kt @@ -1,9 +1,11 @@ package de.mm20.launcher2.calculator +import de.mm20.launcher2.preferences.search.CalculatorSearchSettings import de.mm20.launcher2.search.data.Calculator import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.mariuszgromada.math.mxparser.Expression @@ -12,16 +14,19 @@ interface CalculatorRepository { fun search(query: String): Flow } -class CalculatorRepositoryImpl : CalculatorRepository, KoinComponent { +class CalculatorRepositoryImpl( + private val settings: CalculatorSearchSettings +) : CalculatorRepository, KoinComponent { - override fun search(query: String): Flow = channelFlow { - if (query.isBlank()) { - send(null) - return@channelFlow + override fun search(query: String): Flow { + return settings.enabled.map { + if (it && query.isNotBlank()) { + queryCalculator(query) + } else { + null + } } - - send(queryCalculator(query)) } private suspend fun queryCalculator(query: String): Calculator? { diff --git a/data/calculator/src/main/java/de/mm20/launcher2/calculator/Module.kt b/data/calculator/src/main/java/de/mm20/launcher2/calculator/Module.kt index 2d7399ec..be232690 100644 --- a/data/calculator/src/main/java/de/mm20/launcher2/calculator/Module.kt +++ b/data/calculator/src/main/java/de/mm20/launcher2/calculator/Module.kt @@ -3,5 +3,5 @@ package de.mm20.launcher2.calculator import org.koin.dsl.module val calculatorModule = module { - single { CalculatorRepositoryImpl() } + single { CalculatorRepositoryImpl(get()) } } \ No newline at end of file diff --git a/data/calendar/build.gradle.kts b/data/calendar/build.gradle.kts index 505d60dc..33c7bb56 100644 --- a/data/calendar/build.gradle.kts +++ b/data/calendar/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(project(":core:ktx")) implementation(project(":core:base")) implementation(project(":core:permissions")) + implementation(project(":core:preferences")) implementation(project(":libs:material-color-utilities")) } \ No newline at end of file diff --git a/data/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt b/data/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt index 169c437a..63874231 100644 --- a/data/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt +++ b/data/calendar/src/main/java/de/mm20/launcher2/calendar/CalendarRepository.kt @@ -6,6 +6,7 @@ import android.provider.CalendarContract import androidx.core.database.getStringOrNull import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.preferences.search.CalendarSearchSettings import de.mm20.launcher2.search.CalendarEvent import de.mm20.launcher2.search.SearchableRepository import kotlinx.collections.immutable.ImmutableList @@ -15,12 +16,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import java.util.Calendar -interface CalendarRepository: SearchableRepository { +interface CalendarRepository : SearchableRepository { fun findMany( from: Long = System.currentTimeMillis(), to: Long = from + 14 * 24 * 60 * 60 * 1000L, @@ -35,6 +37,7 @@ interface CalendarRepository: SearchableRepository { internal class CalendarRepositoryImpl( private val context: Context, private val permissionsManager: PermissionsManager, + private val settings: CalendarSearchSettings, ) : CalendarRepository { override fun search(query: String): Flow> { @@ -45,18 +48,21 @@ internal class CalendarRepositoryImpl( } val hasPermission = permissionsManager.hasPermission(PermissionGroup.Calendar) - return hasPermission.map { - if (it) { - val now = System.currentTimeMillis() - queryCalendarEvents( - query, - intervalStart = now, - intervalEnd = now + 14 * 24 * 60 * 60 * 1000L, - ).toImmutableList() - } else { - persistentListOf() + val enabled = settings.enabled + + return hasPermission.combine(enabled) { a, b -> a && b } + .map { + if (it) { + val now = System.currentTimeMillis() + queryCalendarEvents( + query, + intervalStart = now, + intervalEnd = now + 14 * 24 * 60 * 60 * 1000L, + ).toImmutableList() + } else { + persistentListOf() + } } - } } diff --git a/data/calendar/src/main/java/de/mm20/launcher2/calendar/Module.kt b/data/calendar/src/main/java/de/mm20/launcher2/calendar/Module.kt index ddc08e16..4f6fcddd 100644 --- a/data/calendar/src/main/java/de/mm20/launcher2/calendar/Module.kt +++ b/data/calendar/src/main/java/de/mm20/launcher2/calendar/Module.kt @@ -8,7 +8,7 @@ import org.koin.core.qualifier.named import org.koin.dsl.module val calendarModule = module { - factory>(named()) { CalendarRepositoryImpl(androidContext(), get()) } - factory { CalendarRepositoryImpl(androidContext(), get()) } + factory>(named()) { get() } + factory { CalendarRepositoryImpl(androidContext(), get(), get()) } factory(named(AndroidCalendarEvent.Domain)) { CalendarEventDeserializer(androidContext()) } } \ No newline at end of file diff --git a/data/contacts/build.gradle.kts b/data/contacts/build.gradle.kts index 67dec9b9..e3e120c1 100644 --- a/data/contacts/build.gradle.kts +++ b/data/contacts/build.gradle.kts @@ -44,5 +44,6 @@ dependencies { implementation(project(":core:ktx")) implementation(project(":core:base")) implementation(project(":core:permissions")) + implementation(project(":core:preferences")) } \ No newline at end of file diff --git a/data/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt b/data/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt index d7c3e0e2..b0a0e0d2 100644 --- a/data/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt +++ b/data/contacts/src/main/java/de/mm20/launcher2/contacts/ContactRepository.kt @@ -5,6 +5,7 @@ import android.provider.ContactsContract import androidx.core.database.getStringOrNull import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager +import de.mm20.launcher2.preferences.search.ContactSearchSettings import de.mm20.launcher2.search.Contact import de.mm20.launcher2.search.ContactInfo import de.mm20.launcher2.search.SearchableRepository @@ -12,12 +13,16 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext internal class ContactRepository( private val context: Context, - private val permissionsManager: PermissionsManager + private val permissionsManager: PermissionsManager, + private val settings: ContactSearchSettings, ) : SearchableRepository { fun get(id: Long): Flow = flow { @@ -44,114 +49,120 @@ internal class ContactRepository( emit(getWithRawIds(id, rawContacts)) } - private suspend fun getWithRawIds(id: Long, rawIds: Set): Contact? = withContext(Dispatchers.IO) { - val s = "(" + rawIds.joinToString(separator = " OR ", - transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" + - " AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${TelegramContactInfo.ItemType}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${WhatsAppContactInfo.ItemType}\"" + - " OR ${ContactsContract.Data.MIMETYPE} = \"${SignalContactInfo.ItemType}\"" + - ")" - val dataCursor = context.contentResolver.query( - ContactsContract.Data.CONTENT_URI, - null, s, null, null - ) ?: return@withContext null - val contactInfos = mutableSetOf() - var firstName = "" - var lastName = "" - var displayName = "" - val mimeTypeColumn = dataCursor.getColumnIndex(ContactsContract.Data.MIMETYPE) - val emailAddressColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) - val numberColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) - val addressColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) - val displayNameColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME) - val givenNameColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) - val familyNameColumn = - dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) - val data1Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA1) - val data3Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA3) - val idColumn = dataCursor.getColumnIndex(ContactsContract.Data._ID) - loop@ while (dataCursor.moveToNext()) { - when (dataCursor.getStringOrNull(mimeTypeColumn)) { - ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> - dataCursor.getStringOrNull(emailAddressColumn)?.let { - contactInfos.add(MailContactInfo(it)) + private suspend fun getWithRawIds(id: Long, rawIds: Set): Contact? = + withContext(Dispatchers.IO) { + val s = "(" + rawIds.joinToString(separator = " OR ", + transform = { "${ContactsContract.Data.RAW_CONTACT_ID} = $it" }) + ")" + + " AND (${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${TelegramContactInfo.ItemType}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${WhatsAppContactInfo.ItemType}\"" + + " OR ${ContactsContract.Data.MIMETYPE} = \"${SignalContactInfo.ItemType}\"" + + ")" + val dataCursor = context.contentResolver.query( + ContactsContract.Data.CONTENT_URI, + null, s, null, null + ) ?: return@withContext null + val contactInfos = mutableSetOf() + var firstName = "" + var lastName = "" + var displayName = "" + val mimeTypeColumn = dataCursor.getColumnIndex(ContactsContract.Data.MIMETYPE) + val emailAddressColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS) + val numberColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER) + val addressColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) + val displayNameColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME) + val givenNameColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME) + val familyNameColumn = + dataCursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME) + val data1Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA1) + val data3Column = dataCursor.getColumnIndex(ContactsContract.Data.DATA3) + val idColumn = dataCursor.getColumnIndex(ContactsContract.Data._ID) + loop@ while (dataCursor.moveToNext()) { + when (dataCursor.getStringOrNull(mimeTypeColumn)) { + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(emailAddressColumn)?.let { + contactInfos.add(MailContactInfo(it)) + } + + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(numberColumn)?.let { + val phone = it.replace(Regex("[^+0-9]"), "") + contactInfos.add(PhoneContactInfo(phone)) + } + + ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> + dataCursor.getStringOrNull(addressColumn)?.let { + contactInfos.add(PostalContactInfo(it)) + } + + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> { + firstName = dataCursor.getStringOrNull(givenNameColumn) ?: "" + lastName = dataCursor.getStringOrNull(familyNameColumn) ?: "" + displayName = dataCursor.getStringOrNull(displayNameColumn) ?: "" } - ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> - dataCursor.getStringOrNull(numberColumn)?.let { - val phone = it.replace(Regex("[^+0-9]"), "") - contactInfos.add(PhoneContactInfo(phone)) + TelegramContactInfo.ItemType -> { + val data1 = dataCursor.getStringOrNull(data1Column) + ?: continue@loop + val data3 = dataCursor.getStringOrNull(data3Column) + ?: continue@loop + contactInfos.add( + TelegramContactInfo(data3.substringAfterLast(" "), data1) + ) } - ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE -> - dataCursor.getStringOrNull(addressColumn)?.let { - contactInfos.add(PostalContactInfo(it)) + WhatsAppContactInfo.ItemType -> { + val data1 = dataCursor.getStringOrNull(data1Column) + ?: continue@loop + val dataId = dataCursor.getLong(idColumn) + contactInfos.add( + WhatsAppContactInfo( + "+${data1.substringBefore('@')}", + dataId + ) + ) } - ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> { - firstName = dataCursor.getStringOrNull(givenNameColumn) ?: "" - lastName = dataCursor.getStringOrNull(familyNameColumn) ?: "" - displayName = dataCursor.getStringOrNull(displayNameColumn) ?: "" - } - - TelegramContactInfo.ItemType -> { - val data1 = dataCursor.getStringOrNull(data1Column) - ?: continue@loop - val data3 = dataCursor.getStringOrNull(data3Column) - ?: continue@loop - contactInfos.add( - TelegramContactInfo(data3.substringAfterLast(" "), data1) - ) - } - - WhatsAppContactInfo.ItemType -> { - val data1 = dataCursor.getStringOrNull(data1Column) - ?: continue@loop - val dataId = dataCursor.getLong(idColumn) - contactInfos.add(WhatsAppContactInfo("+${data1.substringBefore('@')}", dataId)) - } - - SignalContactInfo.ItemType -> { - val data1 = dataCursor.getStringOrNull(data1Column) - ?: continue@loop - val dataId = dataCursor.getLong(idColumn) - contactInfos.add(SignalContactInfo(data1, dataId)) + SignalContactInfo.ItemType -> { + val data1 = dataCursor.getStringOrNull(data1Column) + ?: continue@loop + val dataId = dataCursor.getLong(idColumn) + contactInfos.add(SignalContactInfo(data1, dataId)) + } } } - } - dataCursor.close() + dataCursor.close() - val lookupKeyCursor = context.contentResolver.query( - ContactsContract.Contacts.CONTENT_URI, - arrayOf(ContactsContract.Contacts.LOOKUP_KEY), - "${ContactsContract.Contacts._ID} = ?", - arrayOf(id.toString()), - null - ) ?: return@withContext null - var lookUpKey = "" - if (lookupKeyCursor.moveToNext()) { - lookUpKey = lookupKeyCursor.getString(0) - } - lookupKeyCursor.close() + val lookupKeyCursor = context.contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + arrayOf(ContactsContract.Contacts.LOOKUP_KEY), + "${ContactsContract.Contacts._ID} = ?", + arrayOf(id.toString()), + null + ) ?: return@withContext null + var lookUpKey = "" + if (lookupKeyCursor.moveToNext()) { + lookUpKey = lookupKeyCursor.getString(0) + } + lookupKeyCursor.close() - return@withContext AndroidContact( - id = id, - firstName = firstName, - lastName = lastName, - displayName = displayName, - contactInfos = contactInfos, - lookupKey = lookUpKey - ) - } + return@withContext AndroidContact( + id = id, + firstName = firstName, + lastName = lastName, + displayName = displayName, + contactInfos = contactInfos, + lookupKey = lookUpKey + ) + } override fun search(query: String): Flow> { val hasPermission = permissionsManager.hasPermission(PermissionGroup.Contacts) @@ -162,7 +173,7 @@ internal class ContactRepository( } } - return hasPermission.map { + return hasPermission.combine(settings.enabled) { perm, en -> perm && en }.map { if (it) { queryContacts(query) } else { diff --git a/data/contacts/src/main/java/de/mm20/launcher2/contacts/Module.kt b/data/contacts/src/main/java/de/mm20/launcher2/contacts/Module.kt index 1f4dbd4b..7d68328d 100644 --- a/data/contacts/src/main/java/de/mm20/launcher2/contacts/Module.kt +++ b/data/contacts/src/main/java/de/mm20/launcher2/contacts/Module.kt @@ -8,7 +8,7 @@ import org.koin.core.qualifier.named import org.koin.dsl.module val contactsModule = module { - factory { ContactRepository(androidContext(), get()) } - factory>(named()) { ContactRepository(androidContext(), get()) } + factory { ContactRepository(androidContext(), get(), get()) } + factory>(named()) { get() } factory(named(AndroidContact.Domain)) { ContactDeserializer(get(), get()) } } \ No newline at end of file diff --git a/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt b/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt index 8b0f0316..06d6c6c4 100644 --- a/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt +++ b/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt @@ -152,7 +152,7 @@ abstract class AppDatabase : RoomDatabase() { Migration_21_22(), Migration_22_23(), Migration_23_24(), - Migration_24_25(context), + Migration_24_25(), Migration_25_26(), ).build() if (_instance == null) _instance = instance diff --git a/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_24_25.kt b/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_24_25.kt index ab0c649b..b7e9a08e 100644 --- a/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_24_25.kt +++ b/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_24_25.kt @@ -1,217 +1,12 @@ package de.mm20.launcher2.database.migrations -import android.content.Context import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import de.mm20.launcher2.database.R -import de.mm20.launcher2.ktx.toBytes -import de.mm20.launcher2.preferences.LauncherDataStore -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.runBlocking import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import java.util.UUID -class Migration_24_25( - private val context: Context, -) : Migration(24, 25), KoinComponent { - private val dataStore: LauncherDataStore by inject() +class Migration_24_25 : Migration(24, 25), KoinComponent { override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL( - """ - CREATE TABLE IF NOT EXISTS `Theme` ( - `id` BLOB NOT NULL, - `name` TEXT NOT NULL, - - `corePaletteA1` INTEGER, - `corePaletteA2` INTEGER, - `corePaletteA3` INTEGER, - `corePaletteN1` INTEGER, - `corePaletteN2` INTEGER, - `corePaletteE` INTEGER, - `lightPrimary` TEXT, - `lightOnPrimary` TEXT, - `lightPrimaryContainer` TEXT, - `lightOnPrimaryContainer` TEXT, - `lightSecondary` TEXT, - `lightOnSecondary` TEXT, - `lightSecondaryContainer` TEXT, - `lightOnSecondaryContainer` TEXT, - `lightTertiary` TEXT, - `lightOnTertiary` TEXT, - `lightTertiaryContainer` TEXT, - `lightOnTertiaryContainer` TEXT, - `lightError` TEXT, - `lightOnError` TEXT, - `lightErrorContainer` TEXT, - `lightOnErrorContainer` TEXT, - `lightSurface` TEXT, - `lightOnSurface` TEXT, - `lightOnSurfaceVariant` TEXT, - `lightOutline` TEXT, - `lightOutlineVariant` TEXT, - `lightInverseSurface` TEXT, - `lightInverseOnSurface` TEXT, - `lightInversePrimary` TEXT, - `lightSurfaceDim` TEXT, - `lightSurfaceBright` TEXT, - `lightSurfaceContainerLowest` TEXT, - `lightSurfaceContainerLow` TEXT, - `lightSurfaceContainer` TEXT, - `lightSurfaceContainerHigh` TEXT, - `lightSurfaceContainerHighest` TEXT, - `lightBackground` TEXT, - `lightOnBackground` TEXT, - `lightSurfaceTint` TEXT, - `lightScrim` TEXT, - `lightSurfaceVariant` TEXT, - - `darkPrimary` TEXT, - `darkOnPrimary` TEXT, - `darkPrimaryContainer` TEXT, - `darkOnPrimaryContainer` TEXT, - `darkSecondary` TEXT, - `darkOnSecondary` TEXT, - `darkSecondaryContainer` TEXT, - `darkOnSecondaryContainer` TEXT, - `darkTertiary` TEXT, - `darkOnTertiary` TEXT, - `darkTertiaryContainer` TEXT, - `darkOnTertiaryContainer` TEXT, - `darkError` TEXT, - `darkOnError` TEXT, - `darkErrorContainer` TEXT, - `darkOnErrorContainer` TEXT, - `darkSurface` TEXT, - `darkOnSurface` TEXT, - `darkOnSurfaceVariant` TEXT, - `darkOutline` TEXT, - `darkOutlineVariant` TEXT, - `darkInverseSurface` TEXT, - `darkInverseOnSurface` TEXT, - `darkInversePrimary` TEXT, - `darkSurfaceDim` TEXT, - `darkSurfaceBright` TEXT, - `darkSurfaceContainerLowest` TEXT, - `darkSurfaceContainerLow` TEXT, - `darkSurfaceContainer` TEXT, - `darkSurfaceContainerHigh` TEXT, - `darkSurfaceContainerHighest` TEXT, - `darkBackground` TEXT, - `darkOnBackground` TEXT, - `darkSurfaceTint` TEXT, - `darkScrim` TEXT, - `darkSurfaceVariant` TEXT, - PRIMARY KEY(`id`) - ) - """.trimIndent() - ) - // Special UUID for migrated custom color scheme. Same UUID is used in data store migration 16..17 - val uuid = UUID(1L, 1L) - val customColors = runBlocking { - dataStore.data.map { it.appearance.customColors }.first() - } - - database.execSQL("""INSERT INTO `Theme` VALUES ( - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,?, - ?,?,?,?,?,?,?,?,?,? - ) - """.trimIndent(), - arrayOf( - uuid.toBytes(), - context.getString(R.string.preference_colors_custom), - customColors.baseColors.accent1.toHexColor(), - customColors.baseColors.accent2.toHexColor(), - customColors.baseColors.accent3.toHexColor(), - customColors.baseColors.neutral1.toHexColor(), - customColors.baseColors.neutral2.toHexColor(), - customColors.baseColors.error.toHexColor(), - customColors.lightScheme.primary.toHexColor(), - customColors.lightScheme.onPrimary.toHexColor(), - customColors.lightScheme.primaryContainer.toHexColor(), - customColors.lightScheme.onPrimaryContainer.toHexColor(), - customColors.lightScheme.secondary.toHexColor(), - customColors.lightScheme.onSecondary.toHexColor(), - customColors.lightScheme.secondaryContainer.toHexColor(), - customColors.lightScheme.onSecondaryContainer.toHexColor(), - customColors.lightScheme.tertiary.toHexColor(), - customColors.lightScheme.onTertiary.toHexColor(), - customColors.lightScheme.tertiaryContainer.toHexColor(), - customColors.lightScheme.onTertiaryContainer.toHexColor(), - customColors.lightScheme.error.toHexColor(), - customColors.lightScheme.onError.toHexColor(), - customColors.lightScheme.errorContainer.toHexColor(), - customColors.lightScheme.onErrorContainer.toHexColor(), - customColors.lightScheme.surface.toHexColor(), - customColors.lightScheme.onSurface.toHexColor(), - customColors.lightScheme.onSurfaceVariant.toHexColor(), - customColors.lightScheme.outline.toHexColor(), - customColors.lightScheme.outlineVariant.toHexColor(), - customColors.lightScheme.inverseSurface.toHexColor(), - customColors.lightScheme.inverseOnSurface.toHexColor(), - customColors.lightScheme.inversePrimary.toHexColor(), - customColors.lightScheme.surfaceDim.toHexColor(), - customColors.lightScheme.surfaceBright.toHexColor(), - customColors.lightScheme.surfaceContainerLowest.toHexColor(), - customColors.lightScheme.surfaceContainerLow.toHexColor(), - customColors.lightScheme.surfaceContainer.toHexColor(), - customColors.lightScheme.surfaceContainerHigh.toHexColor(), - customColors.lightScheme.surfaceContainerHighest.toHexColor(), - customColors.lightScheme.background.toHexColor(), - customColors.lightScheme.onBackground.toHexColor(), - customColors.lightScheme.surfaceTint.toHexColor(), - customColors.lightScheme.scrim.toHexColor(), - customColors.lightScheme.surfaceVariant.toHexColor(), - - customColors.darkScheme.primary.toHexColor(), - customColors.darkScheme.onPrimary.toHexColor(), - customColors.darkScheme.primaryContainer.toHexColor(), - customColors.darkScheme.onPrimaryContainer.toHexColor(), - customColors.darkScheme.secondary.toHexColor(), - customColors.darkScheme.onSecondary.toHexColor(), - customColors.darkScheme.secondaryContainer.toHexColor(), - customColors.darkScheme.onSecondaryContainer.toHexColor(), - customColors.darkScheme.tertiary.toHexColor(), - customColors.darkScheme.onTertiary.toHexColor(), - customColors.darkScheme.tertiaryContainer.toHexColor(), - customColors.darkScheme.onTertiaryContainer.toHexColor(), - customColors.darkScheme.error.toHexColor(), - customColors.darkScheme.onError.toHexColor(), - customColors.darkScheme.errorContainer.toHexColor(), - customColors.darkScheme.onErrorContainer.toHexColor(), - customColors.darkScheme.surface.toHexColor(), - customColors.darkScheme.onSurface.toHexColor(), - customColors.darkScheme.onSurfaceVariant.toHexColor(), - customColors.darkScheme.outline.toHexColor(), - customColors.darkScheme.outlineVariant.toHexColor(), - customColors.darkScheme.inverseSurface.toHexColor(), - customColors.darkScheme.inverseOnSurface.toHexColor(), - customColors.darkScheme.inversePrimary.toHexColor(), - customColors.darkScheme.surfaceDim.toHexColor(), - customColors.darkScheme.surfaceBright.toHexColor(), - customColors.darkScheme.surfaceContainerLowest.toHexColor(), - customColors.darkScheme.surfaceContainerLow.toHexColor(), - customColors.darkScheme.surfaceContainer.toHexColor(), - customColors.darkScheme.surfaceContainerHigh.toHexColor(), - customColors.darkScheme.surfaceContainerHighest.toHexColor(), - customColors.darkScheme.background.toHexColor(), - customColors.darkScheme.onBackground.toHexColor(), - customColors.darkScheme.surfaceTint.toHexColor(), - customColors.darkScheme.scrim.toHexColor(), - customColors.darkScheme.surfaceVariant.toHexColor(), - ) - ) - } - - fun Int.toHexColor(): String { - return "#${toUInt().toString(16).padStart(6, '0')}" + // removed } } \ No newline at end of file diff --git a/data/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt b/data/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt index 7a6341dc..bdb65c9a 100644 --- a/data/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt +++ b/data/files/src/main/java/de/mm20/launcher2/files/FilesRepository.kt @@ -1,36 +1,26 @@ package de.mm20.launcher2.files import android.content.Context -import de.mm20.launcher2.files.providers.FileProvider import de.mm20.launcher2.files.providers.GDriveFileProvider import de.mm20.launcher2.files.providers.LocalFileProvider import de.mm20.launcher2.files.providers.NextcloudFileProvider import de.mm20.launcher2.files.providers.OwncloudFileProvider import de.mm20.launcher2.files.providers.PluginFileProvider -import de.mm20.launcher2.files.settings.FileSearchSettings import de.mm20.launcher2.nextcloud.NextcloudApiHelper import de.mm20.launcher2.owncloud.OwncloudClient import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.plugin.PluginRepository -import de.mm20.launcher2.plugin.PluginType -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.FileSearchSettings import de.mm20.launcher2.search.File import de.mm20.launcher2.search.SearchableRepository import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map internal class FileRepository( private val context: Context, private val permissionsManager: PermissionsManager, private val settings: FileSearchSettings, - private val pluginRepository: PluginRepository, ) : SearchableRepository { private val nextcloudClient by lazy { @@ -48,26 +38,15 @@ internal class FileRepository( return@channelFlow } - val filePlugins = pluginRepository.findMany( - type = PluginType.FileSearch, - enabled = true, - ) - - settings.data.collectLatest { settings -> - val providers = mutableListOf() - - if (settings.localFiles) providers.add( - LocalFileProvider( - context, - permissionsManager - ) - ) - if (settings.gdriveFiles) providers.add(GDriveFileProvider(context)) - if (settings.nextcloudFiles) providers.add(NextcloudFileProvider(nextcloudClient)) - if (settings.owncloudFiles) providers.add(OwncloudFileProvider(owncloudClient)) - - for (plugin in settings.plugins) { - providers.add(PluginFileProvider(context, plugin)) + settings.enabledProviders.collectLatest { providerIds -> + val providers = providerIds.map { + when (it) { + "local" -> LocalFileProvider(context, permissionsManager) + "gdrive" -> GDriveFileProvider(context) + "nextcloud" -> NextcloudFileProvider(nextcloudClient) + "owncloud" -> OwncloudFileProvider(owncloudClient) + else -> PluginFileProvider(context, it) + } } if (providers.isEmpty()) { diff --git a/data/files/src/main/java/de/mm20/launcher2/files/Module.kt b/data/files/src/main/java/de/mm20/launcher2/files/Module.kt index 8b86a310..1691e75a 100644 --- a/data/files/src/main/java/de/mm20/launcher2/files/Module.kt +++ b/data/files/src/main/java/de/mm20/launcher2/files/Module.kt @@ -1,13 +1,11 @@ package de.mm20.launcher2.files -import de.mm20.launcher2.backup.Backupable import de.mm20.launcher2.files.providers.GDriveFile import de.mm20.launcher2.files.providers.LocalFile import de.mm20.launcher2.files.providers.NextcloudFile import de.mm20.launcher2.files.providers.OneDriveFile import de.mm20.launcher2.files.providers.OwncloudFile import de.mm20.launcher2.files.providers.PluginFile -import de.mm20.launcher2.files.settings.FileSearchSettings import de.mm20.launcher2.search.File import de.mm20.launcher2.search.SearchableDeserializer import de.mm20.launcher2.search.SearchableRepository @@ -20,7 +18,6 @@ val filesModule = module { FileRepository( androidContext(), get(), - get(), get() ) } @@ -35,6 +32,4 @@ val filesModule = module { get() ) } - single { FileSearchSettings(androidContext(), get()) } - factory(named()) { get() } } \ No newline at end of file diff --git a/data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettings.kt b/data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettings.kt deleted file mode 100644 index ec47dea2..00000000 --- a/data/files/src/main/java/de/mm20/launcher2/files/settings/FileSearchSettings.kt +++ /dev/null @@ -1,100 +0,0 @@ -package de.mm20.launcher2.files.settings - -import android.content.Context -import androidx.datastore.dataStore -import de.mm20.launcher2.backup.Backupable -import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.files.settings.migrations.Migration1 -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.settings.BaseSettings -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import java.io.File - -class FileSearchSettings( - private val context: Context, - dataStore: LauncherDataStore, -) : BaseSettings( - context = context, - fileName = "file_search.json", - serializer = FileSearchSettingsDataSerializer, - migrations = listOf( - Migration1(dataStore), - ) -) { - - internal val data - get() = context.dataStore.data - - val localFiles - get(): Flow { - return context.dataStore.data.map { it.localFiles } - } - - fun setLocalFiles(localFiles: Boolean) { - updateData { - it.copy(localFiles = localFiles) - } - } - - val gdriveFiles - get(): Flow { - return context.dataStore.data.map { it.gdriveFiles } - } - - fun setGdriveFiles(gdriveFiles: Boolean) { - updateData { - it.copy(gdriveFiles = gdriveFiles) - } - } - - val nextcloudFiles - get(): Flow { - return context.dataStore.data.map { it.nextcloudFiles } - } - - fun setNextcloudFiles(nextcloudFiles: Boolean) { - updateData { - it.copy(nextcloudFiles = nextcloudFiles) - } - } - - val owncloudFiles - get(): Flow { - return context.dataStore.data.map { it.owncloudFiles } - } - - fun setOwncloudFiles(owncloudFiles: Boolean) { - updateData { - it.copy(owncloudFiles = owncloudFiles) - } - } - - val enabledPlugins: Flow> - get(): Flow> { - return context.dataStore.data.map { it.plugins } - } - - fun setEnabledPlugins(enabledPlugins: Set) { - updateData { - it.copy(plugins = enabledPlugins) - } - } - - fun setPluginEnabled(authority: String, enabled: Boolean) { - updateData { - if (enabled) { - it.copy(plugins = it.plugins + authority) - } else { - it.copy(plugins = it.plugins - authority) - } - } - } -} \ No newline at end of file diff --git a/data/files/src/main/java/de/mm20/launcher2/files/settings/migrations/Migration1.kt b/data/files/src/main/java/de/mm20/launcher2/files/settings/migrations/Migration1.kt deleted file mode 100644 index c9911dbf..00000000 --- a/data/files/src/main/java/de/mm20/launcher2/files/settings/migrations/Migration1.kt +++ /dev/null @@ -1,33 +0,0 @@ -package de.mm20.launcher2.files.settings.migrations - -import androidx.datastore.core.DataMigration -import de.mm20.launcher2.files.settings.FileSearchSettingsData -import de.mm20.launcher2.preferences.LauncherDataStore -import kotlinx.coroutines.flow.first - -/** - * This migration is used to migrate the data from the old proto data store. - * TODO: remove after a few releases - */ -internal class Migration1( - private val dataStore: LauncherDataStore, -): DataMigration { - override suspend fun cleanUp() { - - } - - override suspend fun shouldMigrate(currentData: FileSearchSettingsData): Boolean { - return currentData.schemaVersion < 1 - } - - override suspend fun migrate(currentData: FileSearchSettingsData): FileSearchSettingsData { - val data = dataStore.data.first().fileSearch - return currentData.copy( - localFiles = data.localFiles, - gdriveFiles = data.gdrive, - nextcloudFiles = data.nextcloud, - owncloudFiles = data.owncloud, - schemaVersion = 1, - ) - } -} \ No newline at end of file diff --git a/data/searchable/src/main/java/de/mm20/launcher2/searchable/Module.kt b/data/searchable/src/main/java/de/mm20/launcher2/searchable/Module.kt index 1b289ddf..19e3014c 100644 --- a/data/searchable/src/main/java/de/mm20/launcher2/searchable/Module.kt +++ b/data/searchable/src/main/java/de/mm20/launcher2/searchable/Module.kt @@ -3,12 +3,14 @@ package de.mm20.launcher2.searchable import de.mm20.launcher2.backup.Backupable import de.mm20.launcher2.search.SearchableDeserializer import de.mm20.launcher2.search.data.Tag -import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.dsl.module val searchableModule = module { - factory (named()) { SavableSearchableRepositoryImpl(androidContext(), get(), get()) } - factory { SavableSearchableRepositoryImpl(androidContext(), get(), get()) } + factory (named()) { SavableSearchableRepositoryImpl( + get(), + get() + ) } + factory { SavableSearchableRepositoryImpl(get(), get()) } factory(named(Tag.Domain)) { TagDeserializer() } } \ No newline at end of file diff --git a/data/searchable/src/main/java/de/mm20/launcher2/searchable/SavableSearchableRepository.kt b/data/searchable/src/main/java/de/mm20/launcher2/searchable/SavableSearchableRepository.kt index 6ad6031d..66e285ad 100644 --- a/data/searchable/src/main/java/de/mm20/launcher2/searchable/SavableSearchableRepository.kt +++ b/data/searchable/src/main/java/de/mm20/launcher2/searchable/SavableSearchableRepository.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.searchable -import android.content.Context import android.util.Log import androidx.room.withTransaction import de.mm20.launcher2.backup.Backupable @@ -9,8 +8,8 @@ import de.mm20.launcher2.database.AppDatabase import de.mm20.launcher2.database.entities.SavedSearchableEntity import de.mm20.launcher2.database.entities.SavedSearchableUpdatePinEntity import de.mm20.launcher2.ktx.jsonObjectOf -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor +import de.mm20.launcher2.preferences.WeightFactor +import de.mm20.launcher2.preferences.search.RankingSettings import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchableDeserializer import kotlinx.coroutines.CoroutineScope @@ -117,9 +116,8 @@ interface SavableSearchableRepository: Backupable { } internal class SavableSearchableRepositoryImpl( - private val context: Context, private val database: AppDatabase, - private val dataStore: LauncherDataStore + private val settings: RankingSettings, ) : SavableSearchableRepository, KoinComponent { private val scope = CoroutineScope(Job() + Dispatchers.Default) @@ -193,7 +191,7 @@ internal class SavableSearchableRepositoryImpl( override fun touch(searchable: SavableSearchable) { scope.launch { val weightFactor = - when (dataStore.data.map { it.resultOrdering.weightFactor }.firstOrNull()) { + when (settings.weightFactor.firstOrNull()) { WeightFactor.Low -> WEIGHT_FACTOR_LOW WeightFactor.High -> WEIGHT_FACTOR_HIGH else -> WEIGHT_FACTOR_MEDIUM diff --git a/data/themes/build.gradle.kts b/data/themes/build.gradle.kts index 18fea445..09b72790 100644 --- a/data/themes/build.gradle.kts +++ b/data/themes/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(project(":core:base")) implementation(project(":data:database")) implementation(project(":core:crashreporter")) + implementation(project(":core:preferences")) implementation(project(":libs:material-color-utilities")) } diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt index 0f72ac9d..5b711983 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt @@ -4,6 +4,7 @@ import android.content.Context import de.mm20.launcher2.backup.Backupable import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.preferences.ThemeDescriptor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -49,9 +50,15 @@ class ThemeRepository( } } - fun getThemeOrDefault(id: UUID?): Flow { - if (id == null) return flowOf(getDefaultTheme()) - return getTheme(id).map { it ?: getDefaultTheme() } + fun getThemeOrDefault(theme: ThemeDescriptor?): Flow { + return when(theme) { + is ThemeDescriptor.BlackAndWhite -> flowOf(getBlackAndWhiteTheme()) + is ThemeDescriptor.Custom -> { + val id = UUID.fromString(theme.id) + getTheme(id).map { it ?: getDefaultTheme() } + } + else -> flowOf(getDefaultTheme()) + } } private fun getBuiltInThemes(): List { diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt index bd80eef5..49836ccf 100644 --- a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt +++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/Module.kt @@ -6,5 +6,5 @@ import org.koin.dsl.module val unitConverterModule = module { single { CurrencyRepository(androidContext()) } - single { UnitConverterRepositoryImpl(androidContext(), get()) } + single { UnitConverterRepositoryImpl(androidContext(), get(), get()) } } \ No newline at end of file diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt index 1d041dc3..e5555bca 100644 --- a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt +++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/UnitConverterRepository.kt @@ -2,44 +2,55 @@ package de.mm20.launcher2.unitconverter import android.content.Context import de.mm20.launcher2.currencies.CurrencyRepository -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.UnitConverterSettings import de.mm20.launcher2.search.data.UnitConverter -import de.mm20.launcher2.unitconverter.converters.* +import de.mm20.launcher2.unitconverter.converters.AreaConverter +import de.mm20.launcher2.unitconverter.converters.CurrencyConverter +import de.mm20.launcher2.unitconverter.converters.DataConverter +import de.mm20.launcher2.unitconverter.converters.LengthConverter +import de.mm20.launcher2.unitconverter.converters.MassConverter +import de.mm20.launcher2.unitconverter.converters.TemperatureConverter +import de.mm20.launcher2.unitconverter.converters.TimeConverter +import de.mm20.launcher2.unitconverter.converters.VelocityConverter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent -import org.koin.core.component.inject interface UnitConverterRepository { - fun search(query: String, includeCurrencies: Boolean): Flow + fun search(query: String): Flow } internal class UnitConverterRepositoryImpl( private val context: Context, private val currencyRepository: CurrencyRepository, + private val settings: UnitConverterSettings, ) : UnitConverterRepository, KoinComponent { - private val dataStore: LauncherDataStore by inject() private val scope = CoroutineScope(Job() + Dispatchers.Default) init { scope.launch { - dataStore.data.map { it.unitConverterSearch }.distinctUntilChanged().collectLatest { - if (it.enabled && it.currencies) currencyRepository.enableCurrencyUpdateWorker() - else currencyRepository.disableCurrencyUpdateWorker() - } + settings.map { it.enabled && it.currencies } + .distinctUntilChanged().collectLatest { + if (it) currencyRepository.enableCurrencyUpdateWorker() + else currencyRepository.disableCurrencyUpdateWorker() + } } } - override fun search(query: String, includeCurrencies: Boolean): Flow = channelFlow { - if (query.isBlank()) { - send(null) - return@channelFlow + override fun search(query: String): Flow { + if (query.isBlank()) return flowOf(null) + return settings.distinctUntilChanged().map { + if (!it.enabled) null + else queryUnitConverter(query, it.currencies) } - send(queryUnitConverter(query, includeCurrencies)) } private suspend fun queryUnitConverter( diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/GeocoderWeatherProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/GeocoderWeatherProvider.kt index db6106b5..740bd96e 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/GeocoderWeatherProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/GeocoderWeatherProvider.kt @@ -4,6 +4,7 @@ import android.content.Context import android.location.Geocoder import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.ktx.formatToString +import de.mm20.launcher2.preferences.weather.WeatherLocation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.IOException diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/Module.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/Module.kt index 7a2497ed..7c172808 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/Module.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/Module.kt @@ -1,20 +1,16 @@ package de.mm20.launcher2.weather -import de.mm20.launcher2.backup.Backupable import de.mm20.launcher2.weather.brightsky.BrightSkyProvider import de.mm20.launcher2.weather.here.HereProvider import de.mm20.launcher2.weather.metno.MetNoProvider import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider import de.mm20.launcher2.weather.plugin.PluginWeatherProvider -import de.mm20.launcher2.weather.settings.WeatherSettings import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.dsl.module val weatherModule = module { single { WeatherRepositoryImpl(androidContext(), get(), get(), get()) } - single { WeatherSettings(androidContext()) } - factory(named()) { get() } factory { (providerId: String) -> when (providerId) { OpenWeatherMapProvider.Id -> OpenWeatherMapProvider(androidContext()) diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherLocation.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherLocation.kt index 65a894ce..dbbf5e5d 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherLocation.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherLocation.kt @@ -1,16 +1,2 @@ package de.mm20.launcher2.weather -sealed interface WeatherLocation { - val name: String - - data class LatLon( - override val name: String, - val lat: Double, - val lon: Double, - ) : WeatherLocation - - data class Id( - override val name: String, - val locationId: String, - ) : WeatherLocation -} \ No newline at end of file diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt index de151c76..0a88d958 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherProvider.kt @@ -1,5 +1,6 @@ package de.mm20.launcher2.weather +import de.mm20.launcher2.preferences.weather.WeatherLocation import org.koin.core.component.KoinComponent import org.koin.core.component.get import org.koin.core.parameter.parametersOf diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt index 32f78841..72c56159 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/WeatherRepository.kt @@ -13,13 +13,13 @@ import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.plugin.PluginRepository import de.mm20.launcher2.plugin.PluginType +import de.mm20.launcher2.preferences.LatLon +import de.mm20.launcher2.preferences.weather.WeatherLocation +import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.weather.brightsky.BrightSkyProvider import de.mm20.launcher2.weather.here.HereProvider import de.mm20.launcher2.weather.metno.MetNoProvider import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider -import de.mm20.launcher2.weather.settings.LatLon -import de.mm20.launcher2.weather.settings.ProviderSettings -import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.koin.core.component.KoinComponent @@ -65,7 +65,7 @@ internal class WeatherRepositoryImpl( } override fun searchLocations(query: String): Flow> { - return settings.data.map { + return settings.map { val provider = WeatherProvider.getInstance(it.provider) provider.findLocation(query) } @@ -86,7 +86,7 @@ internal class WeatherRepositoryImpl( } } scope.launch { - settings.data.collectLatest { + settings.collectLatest { requestUpdate() } } @@ -178,7 +178,7 @@ class WeatherUpdateWorker(val context: Context, params: WorkerParameters) : override suspend fun doWork(): Result { Log.d("WeatherUpdateWorker", "Requesting weather data") - val settingsData = settings.data.first() + val settingsData = settings.first() val provider = WeatherProvider.getInstance(settingsData.provider) val updateInterval = provider.getUpdateInterval() diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/brightsky/BrightSkyProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/brightsky/BrightSkyProvider.kt index c2f2ef39..06e9453a 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/brightsky/BrightSkyProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/brightsky/BrightSkyProvider.kt @@ -5,10 +5,10 @@ import android.icu.text.SimpleDateFormat import android.icu.util.Calendar import android.util.Log import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.preferences.weather.WeatherLocation import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.GeocoderWeatherProvider import de.mm20.launcher2.weather.R -import de.mm20.launcher2.weather.WeatherLocation import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.create diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/here/HereProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/here/HereProvider.kt index db612e21..e8ebce63 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/here/HereProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/here/HereProvider.kt @@ -3,9 +3,9 @@ package de.mm20.launcher2.weather.here import android.content.Context import android.util.Log import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.preferences.weather.WeatherLocation import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.R -import de.mm20.launcher2.weather.WeatherLocation import de.mm20.launcher2.weather.WeatherProvider import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/metno/MetNoProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/metno/MetNoProvider.kt index 657768c1..1560f8cd 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/metno/MetNoProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/metno/MetNoProvider.kt @@ -7,13 +7,11 @@ import android.util.Base64 import android.util.Log import androidx.annotation.WorkerThread import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.preferences.weather.WeatherLocation +import de.mm20.launcher2.preferences.weather.WeatherSettings import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.GeocoderWeatherProvider import de.mm20.launcher2.weather.R -import de.mm20.launcher2.weather.WeatherLocation -import de.mm20.launcher2.weather.WeatherProvider -import de.mm20.launcher2.weather.settings.ProviderSettings -import de.mm20.launcher2.weather.settings.WeatherSettings import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapProvider.kt index 276b3721..49877b1c 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/openweathermap/OpenWeatherMapProvider.kt @@ -3,9 +3,9 @@ package de.mm20.launcher2.weather.openweathermap import android.content.Context import android.util.Log import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.preferences.weather.WeatherLocation import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.R -import de.mm20.launcher2.weather.WeatherLocation import de.mm20.launcher2.weather.WeatherProvider import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/plugin/PluginWeatherProvider.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/plugin/PluginWeatherProvider.kt index e60d05cb..42c2506e 100644 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/plugin/PluginWeatherProvider.kt +++ b/data/weather/src/main/java/de/mm20/launcher2/weather/plugin/PluginWeatherProvider.kt @@ -13,8 +13,8 @@ import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.plugin.config.WeatherPluginConfig import de.mm20.launcher2.plugin.contracts.PluginContract import de.mm20.launcher2.plugin.contracts.WeatherPluginContract +import de.mm20.launcher2.preferences.weather.WeatherLocation import de.mm20.launcher2.weather.Forecast -import de.mm20.launcher2.weather.WeatherLocation import de.mm20.launcher2.weather.WeatherProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettings.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettings.kt deleted file mode 100644 index 6c16bc3e..00000000 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettings.kt +++ /dev/null @@ -1,119 +0,0 @@ -package de.mm20.launcher2.weather.settings - -import android.content.Context -import de.mm20.launcher2.settings.BaseSettings -import de.mm20.launcher2.weather.WeatherLocation -import de.mm20.launcher2.weather.WeatherProviderInfo -import kotlinx.coroutines.flow.map - -class WeatherSettings( - private val context: Context, -) : BaseSettings( - context, - "weather_settings.json", - WeatherSettingsSerializer, - emptyList(), -) { - internal val data - get() = context.dataStore.data - - val location = data.map { - val providerSettings = it.providerSettings[it.provider] - val id = providerSettings?.locationId - val name = providerSettings?.locationName - - if (id != null && name != null) { - WeatherLocation.Id(name, id) - } else if (it.location != null && it.locationName != null) { - WeatherLocation.LatLon(it.locationName, it.location.lat, it.location.lon) - } else { - null - } - } - - val autoLocation = data.map { it.autoLocation } - - fun setLocation(location: WeatherLocation) { - updateData { - val providerSettings = - it.providerSettings.getOrDefault(it.provider, ProviderSettings()) - when (location) { - is WeatherLocation.LatLon -> { - it.copy( - location = LatLon(lat = location.lat, lon = location.lon), - locationName = location.name, - lastUpdate = 0L, - autoLocation = false, - providerSettings = it.providerSettings.toMutableMap().apply { - put( - it.provider, - providerSettings.copy( - locationId = null, - locationName = null, - ) - ) - } - ) - } - - is WeatherLocation.Id -> { - it.copy( - location = null, - locationName = null, - autoLocation = false, - lastUpdate = 0L, - providerSettings = it.providerSettings.toMutableMap().apply { - put( - it.provider, - providerSettings.copy( - locationId = location.locationId, - locationName = location.name - ) - ) - } - ) - } - } - } - } - - fun setLastLocation(location: LatLon) { - updateData { - it.copy( - lastLocation = location, - ) - } - } - - val lastUpdate = data.map { it.lastUpdate } - - fun setLastUpdate(lastUpdate: Long) { - updateData { - it.copy(lastUpdate = lastUpdate) - } - } - - val providerId = data.map { it.provider } - - fun setProvider(provider: WeatherProviderInfo) { - setProviderId(provider.id) - } - - fun setAutoLocation(autoLocation: Boolean) { - updateData { - it.copy( - autoLocation = autoLocation, - lastUpdate = 0L, - ) - } - } - - fun setProviderId(providerId: String) { - updateData { - it.copy( - provider = providerId, - lastUpdate = 0L, - ) - } - } -} \ No newline at end of file diff --git a/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettingsData.kt b/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettingsData.kt deleted file mode 100644 index 41e886b5..00000000 --- a/data/weather/src/main/java/de/mm20/launcher2/weather/settings/WeatherSettingsData.kt +++ /dev/null @@ -1,63 +0,0 @@ -package de.mm20.launcher2.weather.settings - -import androidx.datastore.core.CorruptionException -import androidx.datastore.core.Serializer -import kotlinx.serialization.Serializable -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream -import kotlinx.serialization.json.encodeToStream -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream - - -@Serializable -data class LatLon( - val lat: Double, - val lon: Double, -) - -@Serializable -data class ProviderSettings( - val locationId: String? = null, - val locationName: String? = null, -) - -@Serializable -data class WeatherSettingsData( - val schemaVersion: Int = 1, - val provider: String = "metno", - val autoLocation: Boolean = true, - val location: LatLon? = null, - val locationName: String? = null, - val lastLocation: LatLon? = null, - val lastUpdate: Long = 0L, - val providerSettings: Map = emptyMap(), -) - -internal object WeatherSettingsSerializer : Serializer{ - internal val json = Json { - ignoreUnknownKeys = true - encodeDefaults = true - } - - override val defaultValue: WeatherSettingsData - get() = WeatherSettingsData() - - override suspend fun readFrom(input: InputStream): WeatherSettingsData { - try { - return json.decodeFromStream(input) - } catch (e: IllegalArgumentException) { - throw (CorruptionException("Cannot read json.", e)) - } catch (e: SerializationException) { - throw (CorruptionException("Cannot read json.", e)) - } catch (e: IOException) { - throw (CorruptionException("Cannot read json.", e)) - } - } - - override suspend fun writeTo(t: WeatherSettingsData, output: OutputStream) { - json.encodeToStream(t, output) - } -} \ No newline at end of file diff --git a/data/websites/build.gradle.kts b/data/websites/build.gradle.kts index ff039d23..679c11e4 100644 --- a/data/websites/build.gradle.kts +++ b/data/websites/build.gradle.kts @@ -50,6 +50,7 @@ dependencies { implementation(libs.coil.core) implementation(project(":core:base")) + implementation(project(":core:preferences")) implementation(project(":core:ktx")) } \ No newline at end of file diff --git a/data/websites/src/main/java/de/mm20/launcher2/websites/Module.kt b/data/websites/src/main/java/de/mm20/launcher2/websites/Module.kt index 1a276cf3..d7800698 100644 --- a/data/websites/src/main/java/de/mm20/launcher2/websites/Module.kt +++ b/data/websites/src/main/java/de/mm20/launcher2/websites/Module.kt @@ -8,6 +8,6 @@ import org.koin.core.qualifier.named import org.koin.dsl.module val websitesModule = module { - single>(named()) { WebsiteRepository(androidContext()) } + single>(named()) { WebsiteRepository(androidContext(), get()) } factory(named(WebsiteImpl.Domain)) { WebsiteDeserializer() } } \ No newline at end of file diff --git a/data/websites/src/main/java/de/mm20/launcher2/websites/WebsiteRepository.kt b/data/websites/src/main/java/de/mm20/launcher2/websites/WebsiteRepository.kt index 951e53ff..9b6bc8e7 100644 --- a/data/websites/src/main/java/de/mm20/launcher2/websites/WebsiteRepository.kt +++ b/data/websites/src/main/java/de/mm20/launcher2/websites/WebsiteRepository.kt @@ -4,6 +4,7 @@ import android.content.Context import android.webkit.URLUtil import androidx.compose.runtime.Immutable import androidx.core.graphics.toColorInt +import de.mm20.launcher2.preferences.search.WebsiteSearchSettings import de.mm20.launcher2.search.SearchableRepository import de.mm20.launcher2.search.Website import kotlinx.collections.immutable.ImmutableList @@ -11,6 +12,8 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.withContext import okhttp3.HttpUrl import okhttp3.OkHttpClient @@ -25,7 +28,10 @@ import java.net.URL import java.util.concurrent.TimeUnit -internal class WebsiteRepository(val context: Context) : SearchableRepository { +internal class WebsiteRepository( + val context: Context, + val settings: WebsiteSearchSettings, +) : SearchableRepository { private val httpClient = OkHttpClient .Builder() @@ -34,16 +40,18 @@ internal class WebsiteRepository(val context: Context) : SearchableRepository> = channelFlow { - send(persistentListOf()) - withContext(Dispatchers.IO) { - httpClient.dispatcher.cancelAll() - } - if (query.isBlank()) return@channelFlow + override fun search(query: String): Flow> { + return settings.enabled.transformLatest {enabled -> + emit(persistentListOf()) + withContext(Dispatchers.IO) { + httpClient.dispatcher.cancelAll() + } + if (!enabled || query.isBlank()) return@transformLatest - val website = queryWebsite(query) - website?.let { - send(persistentListOf(it)) + val website = queryWebsite(query) + website?.let { + emit(persistentListOf(it)) + } } } diff --git a/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt b/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt index b3ecfb84..d84d35e5 100644 --- a/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt +++ b/data/widgets/src/main/java/de/mm20/launcher2/widgets/FavoritesWidget.kt @@ -9,6 +9,7 @@ import java.util.UUID @Serializable data class FavoritesWidgetConfig( val editButton: Boolean = true, + val tagsMultiline: Boolean = false, ) data class FavoritesWidget( diff --git a/data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt b/data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt index db535ab6..1c27c779 100644 --- a/data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt +++ b/data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt @@ -2,7 +2,7 @@ package de.mm20.launcher2.wikipedia import android.content.Context import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.search.WikipediaSearchSettings import de.mm20.launcher2.search.Article import de.mm20.launcher2.search.SearchableRepository import kotlinx.collections.immutable.ImmutableList @@ -10,7 +10,6 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import okhttp3.OkHttpClient -import org.koin.core.component.KoinComponent import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit @@ -18,7 +17,7 @@ import java.util.concurrent.TimeUnit internal class WikipediaRepository( private val context: Context, - private val dataStore: LauncherDataStore + private val settings: WikipediaSearchSettings, ) : SearchableRepository
{ private val scope = CoroutineScope(Job() + Dispatchers.Default) @@ -34,8 +33,7 @@ internal class WikipediaRepository( init { scope.launch { - dataStore.data - .map { it.wikipediaSearch.customUrl } + settings.customUrl .distinctUntilChanged() .collectLatest { try { retrofit = Retrofit.Builder() @@ -55,24 +53,27 @@ internal class WikipediaRepository( private lateinit var wikipediaService: WikipediaApi - override fun search(query: String): Flow> = channelFlow { - send(persistentListOf()) - withContext(Dispatchers.IO) { - httpClient.dispatcher.cancelAll() + override fun search(query: String): Flow> { + if (query.length < 4) return flowOf(persistentListOf()) + + return settings.enabled.transformLatest { + emit(persistentListOf()) + withContext(Dispatchers.IO) { + httpClient.dispatcher.cancelAll() + } + + if (!it || !::wikipediaService.isInitialized) return@transformLatest + if (query.isBlank()) return@transformLatest + + val results = queryWikipedia(query) + if (results != null) { + emit(persistentListOf(results)) + } } - if (query.length < 4) return@channelFlow - - if (!::wikipediaService.isInitialized) return@channelFlow - if (query.isBlank()) return@channelFlow - - dataStore.data.map { it.wikipediaSearch.images }.collectLatest { - val wikipedia = queryWikipedia(query, false) - send(wikipedia?.let { persistentListOf(it) } ?: persistentListOf()) - } } - private suspend fun queryWikipedia(query: String, loadImages: Boolean): Wikipedia? { + private suspend fun queryWikipedia(query: String): Wikipedia? { val wikipediaService = wikipediaService val wikipediaUrl = retrofit.baseUrl().toString() @@ -87,9 +88,7 @@ internal class WikipediaRepository( val page = result.query?.pages?.values?.toList()?.getOrNull(0) ?: return null - val image = if (loadImages) { - result.query.pages.values.toList().getOrNull(0)?.thumbnail?.source - } else null + val image = result.query.pages.values.toList().getOrNull(0)?.thumbnail?.source return Wikipedia( label = page.title, diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt index f31ee849..3cf8ad27 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/BadgeService.kt @@ -8,7 +8,7 @@ import de.mm20.launcher2.badges.providers.NotificationBadgeProvider import de.mm20.launcher2.badges.providers.PluginBadgeProvider import de.mm20.launcher2.badges.providers.SuspendedAppsBadgeProvider import de.mm20.launcher2.badges.providers.WorkProfileBadgeProvider -import de.mm20.launcher2.badges.settings.BadgeSettings +import de.mm20.launcher2.preferences.ui.BadgeSettings import de.mm20.launcher2.search.Searchable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -39,7 +39,7 @@ internal class BadgeServiceImpl( init { scope.launch { - settings.data.distinctUntilChanged().collectLatest { + settings.distinctUntilChanged().collectLatest { val providers = mutableListOf() providers += WorkProfileBadgeProvider() if (it.notifications) { diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/Module.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/Module.kt index cd568b4d..db14d15d 100644 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/Module.kt +++ b/services/badges/src/main/java/de/mm20/launcher2/badges/Module.kt @@ -1,13 +1,8 @@ package de.mm20.launcher2.badges -import de.mm20.launcher2.backup.Backupable -import de.mm20.launcher2.badges.settings.BadgeSettings import org.koin.android.ext.koin.androidContext -import org.koin.core.qualifier.named import org.koin.dsl.module val badgesModule = module { single { BadgeServiceImpl(androidContext(), get()) } - single { BadgeSettings(androidContext(), get()) } - factory(named()) { get() } } \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettings.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettings.kt deleted file mode 100644 index 36afa38a..00000000 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettings.kt +++ /dev/null @@ -1,68 +0,0 @@ -package de.mm20.launcher2.badges.settings - -import android.content.Context -import de.mm20.launcher2.badges.settings.migrations.Migration1 -import de.mm20.launcher2.preferences.LauncherDataStore -import de.mm20.launcher2.settings.BaseSettings -import kotlinx.coroutines.flow.map - -class BadgeSettings( - private val context: Context, - dataStore: LauncherDataStore, -) : BaseSettings( - context = context, - fileName = "badges.json", - serializer = BadgeSettingsDataSerializer, - migrations = listOf( - Migration1(dataStore), - ) -) { - - internal val data - get() = context.dataStore.data - - val notifications - get() = context.dataStore.data.map { it.notifications } - - fun setNotifications(notifications: Boolean) { - updateData { - it.copy(notifications = notifications) - } - } - - val suspendedApps - get() = context.dataStore.data.map { it.suspendedApps } - - fun setSuspendedApps(suspendedApps: Boolean) { - updateData { - it.copy(suspendedApps = suspendedApps) - } - } - - val cloudFiles - get() = context.dataStore.data.map { it.cloudFiles } - - fun setCloudFiles(cloudFiles: Boolean) { - updateData { - it.copy(cloudFiles = cloudFiles) - } - } - - val shortcuts - get() = context.dataStore.data.map { it.shortcuts } - - fun setShortcuts(shortcuts: Boolean) { - updateData { - it.copy(shortcuts = shortcuts) - } - } - - val plugins - get() = context.dataStore.data.map { it.plugins } - - fun setPlugins(plugins: Boolean) { - updateData { - it.copy(plugins = plugins) - } - } -} diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettingsData.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettingsData.kt deleted file mode 100644 index 31f0ed67..00000000 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/BadgeSettingsData.kt +++ /dev/null @@ -1,50 +0,0 @@ -package de.mm20.launcher2.badges.settings - -import androidx.datastore.core.CorruptionException -import androidx.datastore.core.Serializer -import de.mm20.launcher2.files.settings.FileSearchSettingsData -import kotlinx.serialization.Serializable -import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream -import kotlinx.serialization.json.encodeToStream -import java.io.IOException -import java.io.InputStream -import java.io.OutputStream - -@Serializable -data class BadgeSettingsData( - val notifications: Boolean = true, - val suspendedApps: Boolean = true, - val cloudFiles: Boolean = true, - val shortcuts: Boolean = true, - val plugins: Boolean = true, - val schemaVersion: Int = 1, -) - -internal object BadgeSettingsDataSerializer : Serializer { - - private val json = Json { - ignoreUnknownKeys = true - encodeDefaults = true - } - - override val defaultValue: BadgeSettingsData - get() = BadgeSettingsData(schemaVersion = 0) - - override suspend fun readFrom(input: InputStream): BadgeSettingsData { - try { - return json.decodeFromStream(input) - } catch (e: IllegalArgumentException) { - throw (CorruptionException("Cannot read json.", e)) - } catch (e: SerializationException) { - throw (CorruptionException("Cannot read json.", e)) - } catch (e: IOException) { - throw (CorruptionException("Cannot read json.", e)) - } - } - - override suspend fun writeTo(t: BadgeSettingsData, output: OutputStream) { - json.encodeToStream(t, output) - } -} \ No newline at end of file diff --git a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/migrations/Migration1.kt b/services/badges/src/main/java/de/mm20/launcher2/badges/settings/migrations/Migration1.kt deleted file mode 100644 index 1c8959b0..00000000 --- a/services/badges/src/main/java/de/mm20/launcher2/badges/settings/migrations/Migration1.kt +++ /dev/null @@ -1,28 +0,0 @@ -package de.mm20.launcher2.badges.settings.migrations - -import androidx.datastore.core.DataMigration -import de.mm20.launcher2.badges.settings.BadgeSettingsData -import de.mm20.launcher2.preferences.LauncherDataStore -import kotlinx.coroutines.flow.first - -class Migration1( - private val dataStore: LauncherDataStore, -): DataMigration { - override suspend fun cleanUp() { - } - - override suspend fun shouldMigrate(currentData: BadgeSettingsData): Boolean { - return currentData.schemaVersion < 1 - } - - override suspend fun migrate(currentData: BadgeSettingsData): BadgeSettingsData { - val data = dataStore.data.first().badges - return currentData.copy( - notifications = data.notifications, - suspendedApps = data.suspendedApps, - cloudFiles = data.cloudFiles, - shortcuts = data.shortcuts, - schemaVersion = 1, - ) - } -} \ No newline at end of file diff --git a/services/icons/src/main/java/de/mm20/launcher2/icons/IconService.kt b/services/icons/src/main/java/de/mm20/launcher2/icons/IconService.kt index 9f741962..68280dc1 100644 --- a/services/icons/src/main/java/de/mm20/launcher2/icons/IconService.kt +++ b/services/icons/src/main/java/de/mm20/launcher2/icons/IconService.kt @@ -31,7 +31,7 @@ import de.mm20.launcher2.icons.transformations.LauncherIconTransformation import de.mm20.launcher2.icons.transformations.LegacyToAdaptiveTransformation import de.mm20.launcher2.icons.transformations.transform import de.mm20.launcher2.ktx.isAtLeastApiLevel -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.ui.IconSettings import de.mm20.launcher2.search.Application import de.mm20.launcher2.search.SavableSearchable import kotlinx.coroutines.CoroutineScope @@ -50,7 +50,7 @@ import kotlinx.coroutines.launch class IconService( val context: Context, private val iconPackManager: IconPackManager, - private val dataStore: LauncherDataStore, + private val settings: IconSettings, private val customAttributesRepository: CustomAttributesRepository, ) { @@ -90,7 +90,7 @@ class IconService( iconPacksUpdated.tryEmit(Unit) scope.launch { - dataStore.data.map { it.icons }.distinctUntilChanged().collectLatest { settings -> + settings.distinctUntilChanged().collectLatest { settings -> iconPacksUpdated.collectLatest { val fallbackProvider = if (settings.themedIcons) { ThemedPlaceholderIconProvider(context) @@ -99,8 +99,8 @@ class IconService( } val providers = mutableListOf() - if (settings.iconPack.isNotBlank()) { - val pack = iconPackManager.getIconPack(settings.iconPack) + if (!settings.iconPack.isNullOrBlank()) { + val pack = iconPackManager.getIconPack(settings.iconPack!!) if (pack != null) { providers.add( IconPackIconProvider( diff --git a/services/music/src/main/java/de/mm20/launcher2/music/Module.kt b/services/music/src/main/java/de/mm20/launcher2/music/Module.kt index 8debd551..f3a0c016 100644 --- a/services/music/src/main/java/de/mm20/launcher2/music/Module.kt +++ b/services/music/src/main/java/de/mm20/launcher2/music/Module.kt @@ -4,5 +4,5 @@ import org.koin.android.ext.koin.androidContext import org.koin.dsl.module val musicModule = module { - single { MusicServiceImpl(androidContext(), get()) } + single { MusicServiceImpl(androidContext(), get(), get()) } } \ No newline at end of file diff --git a/services/music/src/main/java/de/mm20/launcher2/music/MusicService.kt b/services/music/src/main/java/de/mm20/launcher2/music/MusicService.kt index 721d82ef..1aee1535 100644 --- a/services/music/src/main/java/de/mm20/launcher2/music/MusicService.kt +++ b/services/music/src/main/java/de/mm20/launcher2/music/MusicService.kt @@ -4,21 +4,17 @@ import android.app.PendingIntent import android.content.Context import android.content.Intent import android.content.SharedPreferences -import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.Bitmap import android.media.AudioManager import android.media.MediaMetadata import android.media.session.MediaController -import android.media.session.MediaSession import android.media.session.PlaybackState.CustomAction import android.net.Uri import android.os.Handler import android.os.Looper import android.os.SystemClock -import android.service.notification.StatusBarNotification import android.view.KeyEvent -import androidx.core.app.NotificationCompat import androidx.core.content.edit import androidx.core.graphics.drawable.toBitmap import coil.imageLoader @@ -27,7 +23,7 @@ import coil.size.Scale import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.notifications.Notification import de.mm20.launcher2.notifications.NotificationRepository -import de.mm20.launcher2.preferences.LauncherDataStore +import de.mm20.launcher2.preferences.media.MediaSettings import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -47,7 +43,6 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import java.io.IOException interface MusicService { @@ -81,11 +76,11 @@ interface MusicService { internal class MusicServiceImpl( private val context: Context, - notificationRepository: NotificationRepository + notificationRepository: NotificationRepository, + private val settings: MediaSettings, ) : MusicService, KoinComponent { private val scope = CoroutineScope(Job() + Dispatchers.Default) - private val dataStore: LauncherDataStore by inject() private val preferences: SharedPreferences by lazy { context.getSharedPreferences(PREFS, Context.MODE_PRIVATE) @@ -108,12 +103,12 @@ internal class MusicServiceImpl( private val currentMediaController: SharedFlow = combine( notificationRepository.notifications, - dataStore.data.map { it.musicWidget } + settings, ) { notifications, settings -> withContext(Dispatchers.Default) { val musicApps = getEnabledPlayerPackages( - settings.allowListList.toSet(), - settings.denyListList.toSet() + settings.allowList, + settings.denyList, ) val sbn: Notification? = notifications.filter { it.mediaSessionToken != null && musicApps.contains(it.packageName) diff --git a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt index 62ee3e05..42321875 100644 --- a/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt +++ b/services/search/src/main/java/de/mm20/launcher2/search/SearchService.kt @@ -3,15 +3,6 @@ package de.mm20.launcher2.search import de.mm20.launcher2.calculator.CalculatorRepository import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.data.customattrs.utils.withCustomLabels -import de.mm20.launcher2.preferences.Settings -import de.mm20.launcher2.preferences.Settings.AppShortcutSearchSettings -import de.mm20.launcher2.preferences.Settings.CalculatorSearchSettings -import de.mm20.launcher2.preferences.Settings.CalendarSearchSettings -import de.mm20.launcher2.preferences.Settings.ContactsSearchSettings -import de.mm20.launcher2.preferences.Settings.FilesSearchSettings -import de.mm20.launcher2.preferences.Settings.UnitConverterSearchSettings -import de.mm20.launcher2.preferences.Settings.WebsiteSearchSettings -import de.mm20.launcher2.preferences.Settings.WikipediaSearchSettings import de.mm20.launcher2.search.data.Calculator import de.mm20.launcher2.search.data.UnitConverter import de.mm20.launcher2.searchactions.SearchActionService @@ -32,27 +23,6 @@ import kotlinx.coroutines.supervisorScope interface SearchService { fun search( query: String, - shortcuts: AppShortcutSearchSettings = Settings.AppShortcutSearchSettings.newBuilder() - .setEnabled(false) - .build(), - contacts: ContactsSearchSettings = Settings.ContactsSearchSettings.newBuilder() - .setEnabled(false) - .build(), - calendars: CalendarSearchSettings = Settings.CalendarSearchSettings.newBuilder() - .setEnabled(false) - .build(), - calculator: CalculatorSearchSettings = Settings.CalculatorSearchSettings.newBuilder() - .setEnabled(false) - .build(), - unitConverter: UnitConverterSearchSettings = Settings.UnitConverterSearchSettings.newBuilder() - .setEnabled(false) - .build(), - websites: WebsiteSearchSettings = Settings.WebsiteSearchSettings.newBuilder() - .setEnabled(false) - .build(), - wikipedia: WikipediaSearchSettings = Settings.WikipediaSearchSettings.newBuilder() - .setEnabled(false) - .build(), ): Flow } @@ -72,13 +42,6 @@ internal class SearchServiceImpl( override fun search( query: String, - shortcuts: AppShortcutSearchSettings, - contacts: ContactsSearchSettings, - calendars: CalendarSearchSettings, - calculator: CalculatorSearchSettings, - unitConverter: UnitConverterSearchSettings, - websites: WebsiteSearchSettings, - wikipedia: WikipediaSearchSettings, ): Flow = channelFlow { val results = MutableStateFlow(SearchResults()) supervisorScope { @@ -99,82 +62,68 @@ internal class SearchServiceImpl( } } } - if (shortcuts.enabled) { - launch { - appShortcutRepository.search(query) - .withCustomLabels(customAttributesRepository) - .collectLatest { r -> - results.update { - it.copy(shortcuts = r.toImmutableList()) - } - } - } - } - if (contacts.enabled) { - launch { - contactRepository.search(query) - .withCustomLabels(customAttributesRepository) - .collectLatest { r -> - results.update { - it.copy(contacts = r.toImmutableList()) - } - } - } - } - if (calendars.enabled) { - launch { - calendarRepository.search(query) - .withCustomLabels(customAttributesRepository) - .collectLatest { r -> - results.update { - it.copy(calendars = r.toImmutableList()) - } - } - } - } - if (calculator.enabled) { - launch { - calculatorRepository.search(query).collectLatest { r -> + launch { + appShortcutRepository.search(query) + .withCustomLabels(customAttributesRepository) + .collectLatest { r -> results.update { - it.copy(calculators = r?.let { persistentListOf(it) } - ?: persistentListOf()) + it.copy(shortcuts = r.toImmutableList()) } } + } + launch { + contactRepository.search(query) + .withCustomLabels(customAttributesRepository) + .collectLatest { r -> + results.update { + it.copy(contacts = r.toImmutableList()) + } + } + } + launch { + calendarRepository.search(query) + .withCustomLabels(customAttributesRepository) + .collectLatest { r -> + results.update { + it.copy(calendars = r.toImmutableList()) + } + } + } + launch { + calculatorRepository.search(query).collectLatest { r -> + results.update { + it.copy(calculators = r?.let { persistentListOf(it) } + ?: persistentListOf()) + } } } - if (unitConverter.enabled) { - launch { - unitConverterRepository.search(query, unitConverter.currencies) - .collectLatest { r -> - results.update { - it.copy(unitConverters = r?.let { persistentListOf(it) } - ?: persistentListOf()) - } + launch { + unitConverterRepository.search(query) + .collectLatest { r -> + results.update { + it.copy(unitConverters = r?.let { persistentListOf(it) } + ?: persistentListOf()) } - } + } } - if (websites.enabled) { - launch { - websiteRepository.search(query) - .withCustomLabels(customAttributesRepository) - .collectLatest { r -> - results.update { - it.copy(websites = r.toImmutableList()) - } + launch { + websiteRepository.search(query) + .withCustomLabels(customAttributesRepository) + .collectLatest { r -> + results.update { + it.copy(websites = r.toImmutableList()) } - } + } } - if (wikipedia.enabled) { - launch { - delay(750) - articleRepository.search(query) - .withCustomLabels(customAttributesRepository) - .collectLatest { r -> - results.update { - it.copy(wikipedia = r.toImmutableList()) - } + launch { + delay(750) + articleRepository.search(query) + .withCustomLabels(customAttributesRepository) + .collectLatest { r -> + results.update { + it.copy(wikipedia = r.toImmutableList()) } - } + } } launch { fileRepository.search( From c16969ac5f3fe707022a96695ae74699306d853e Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:32:12 +0100 Subject: [PATCH 33/39] Remove old data store backup component --- .../launcher2/preferences/ImportExport.kt | 65 ------------------- .../de/mm20/launcher2/preferences/Module.kt | 1 - 2 files changed, 66 deletions(-) delete mode 100644 core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt deleted file mode 100644 index f29b823b..00000000 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ImportExport.kt +++ /dev/null @@ -1,65 +0,0 @@ -package de.mm20.launcher2.preferences - -import android.content.Context -import androidx.datastore.core.DataStoreFactory -import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler -import de.mm20.launcher2.backup.Backupable -import de.mm20.launcher2.crashreporter.CrashReporter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.firstOrNull -import java.io.File - -internal class LauncherStoreBackupComponent( - private val context: Context, - private val dataStore: LegacyDataStore -): Backupable { - override suspend fun backup(toDir: File) { - dataStore.export(toDir) - } - - override suspend fun restore(fromDir: File) { - dataStore.import(context, fromDir) - } -} - -suspend fun LegacyDataStore.export(toDir: File) { - val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - val backupDataStore = DataStoreFactory.create( - serializer = LegacySettingsSerializer, - produceFile = { - File(toDir, "settings") - }, - scope = scope - ) - val settings = this.data.firstOrNull() ?: return - backupDataStore.updateData { - settings - } - scope.cancel() -} - - -suspend fun LegacyDataStore.import(context: Context, fromDir: File) { - val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - val backupDataStore = DataStoreFactory.create( - serializer = LegacySettingsSerializer, - migrations = getMigrations(context), - corruptionHandler = ReplaceFileCorruptionHandler { - CrashReporter.logException(it) - LegacySettings.getDefaultInstance() - }, - produceFile = { - File(fromDir, "settings") - }, - scope = scope - ) - val settings = backupDataStore.data.firstOrNull() ?: return - - this.updateData { - settings - } - scope.cancel() -} \ No newline at end of file diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt index 4f3515c5..87c386f7 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/Module.kt @@ -26,7 +26,6 @@ import org.koin.dsl.module val preferencesModule = module { single { androidContext().legacyDataStore } - factory(named()) { LauncherStoreBackupComponent(androidContext(), get()) } single { LauncherDataStore(androidContext(), get()) } factory(named()) { get() } factory { MediaSettings(get()) } From 8d490b3c05ebca5dff85f22af03ca41ebd9b9b24 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:33:07 +0100 Subject: [PATCH 34/39] Bump backup format version number --- .../src/main/java/de/mm20/launcher2/backup/BackupManager.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt b/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt index 2dba5a5a..f963ef5c 100644 --- a/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt +++ b/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt @@ -139,10 +139,11 @@ class BackupManager( /** * Format changelog: * - 1.5: added `weight` to favorites + * - 1.9: migrate from proto to json data store */ private const val BackupFormatMajor = 1 - private const val BackupFormatMinor = 8 + private const val BackupFormatMinor = 9 internal const val BackupFormat = "$BackupFormatMajor.$BackupFormatMinor" } } From 33645ec547ec4222fd8e1d75877570ad1150623e Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:44:47 +0100 Subject: [PATCH 35/39] Restore favorites widget tag list expand functionality --- .../launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt index 9ac7a142..766bd055 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/favorites/FavoritesWidgetVM.kt @@ -40,6 +40,7 @@ class FavoritesWidgetVM : FavoritesVM() { } override fun setTagsExpanded(expanded: Boolean) { + this.tagsExpanded.value = expanded val widget = this.widget.value ?: return widgetsService.updateWidget( widget.copy( From 2492e22f6f04a0578e0df9f35a3a49b90a594834 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:46:23 +0100 Subject: [PATCH 36/39] Fix search favorites tags expand button breaking when activity is recreated Fix #596 --- .../launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt index 599e9bbe..3310bf43 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavoritesVM.kt @@ -14,7 +14,6 @@ class SearchFavoritesVM : FavoritesVM() { private val uiState: UiState by inject() override val tagsExpanded: Flow = uiState.favoritesTagsExpanded - .shareIn(viewModelScope, SharingStarted.Lazily) override fun setTagsExpanded(expanded: Boolean) { uiState.setFavoritesTagsExpanded(expanded) From f8f82e6294403cb2c2a72d0a503efd9006e50dd8 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:54:03 +0100 Subject: [PATCH 37/39] Add decimeter as unit of length Close #650 --- core/i18n/src/main/res/values/units.xml | 5 +++++ .../launcher2/unitconverter/converters/LengthConverter.kt | 5 +++++ .../unitconverter/converters/SimpleFactorConverter.kt | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/core/i18n/src/main/res/values/units.xml b/core/i18n/src/main/res/values/units.xml index 1f8d810c..5ab583d9 100644 --- a/core/i18n/src/main/res/values/units.xml +++ b/core/i18n/src/main/res/values/units.xml @@ -14,6 +14,11 @@ kilometer kilometers + dm + + decimeter + decimeters + cm centimeter diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/LengthConverter.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/LengthConverter.kt index 7248217e..7f8ad914 100644 --- a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/LengthConverter.kt +++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/LengthConverter.kt @@ -18,6 +18,11 @@ class LengthConverter(context: Context) : SimpleFactorConverter() { context.getString(R.string.unit_kilometer_symbol), R.plurals.unit_kilometer ), + MeasureUnitWithFactor( + 10.0, + context.getString(R.string.unit_decimeter_symbol), + R.plurals.unit_decimeter + ), MeasureUnitWithFactor( 100.0, context.getString(R.string.unit_centimeter_symbol), diff --git a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/SimpleFactorConverter.kt b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/SimpleFactorConverter.kt index ce47bd8a..13d6cb1b 100644 --- a/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/SimpleFactorConverter.kt +++ b/data/unitconverter/src/main/java/de/mm20/launcher2/unitconverter/converters/SimpleFactorConverter.kt @@ -7,7 +7,7 @@ import de.mm20.launcher2.unitconverter.MeasureUnit import de.mm20.launcher2.unitconverter.UnitValue /** - * A converter for units that can converted into each other by simply multiplicating with a constant factor + * A converter for units that can converted into each other by simple multiplication with a constant factor */ abstract class SimpleFactorConverter: Converter { open val standardUnits: List = emptyList() From 6989c90fbe28ddb72357215f79bd43fd330ce03f Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:04:54 +0100 Subject: [PATCH 38/39] Use internal cache dir as temporary backup dir --- .../src/main/java/de/mm20/launcher2/backup/BackupManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt b/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt index f963ef5c..57e698a8 100644 --- a/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt +++ b/services/backup/src/main/java/de/mm20/launcher2/backup/BackupManager.kt @@ -36,7 +36,7 @@ class BackupManager( withContext(Dispatchers.IO) { val outputStream = context.contentResolver.openOutputStream(uri) ?: return@withContext null - val backupDir = File(context.externalCacheDir, "backup") + val backupDir = File(context.cacheDir, "backup") if (backupDir.exists()) { backupDir.deleteRecursively() } From b433e5884cb59e65dd29568a00d4974fd3d279fa Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 19 Jan 2024 23:24:18 +0100 Subject: [PATCH 39/39] Fix tag editor apps not loading --- .../java/de/mm20/launcher2/ui/settings/tags/EditTagSheetVM.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/tags/EditTagSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/tags/EditTagSheetVM.kt index c9e783bc..8d0c1786 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/tags/EditTagSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/tags/EditTagSheetVM.kt @@ -55,7 +55,7 @@ class EditTagSheetVM : ViewModel(), KoinComponent { viewModelScope.launch(Dispatchers.Default) { allTags = tagService.getAllTags().first().toSet() val items = if (tag != null) tagService.getTaggedItems(tag).first() else emptyList() - val apps = appRepository.findMany().first().sorted() + val apps = appRepository.findMany().first { it.isNotEmpty() }.sorted() taggedItems = items taggableApps = apps.map { app -> TaggableItem(app, items.any { app.key == it.key }) } taggableOther = items.mapNotNull { item ->