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(