Refactor preferences module

This commit is contained in:
MM20 2024-01-19 22:29:37 +01:00
parent 94aee6baba
commit 92ddc75060
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
171 changed files with 3202 additions and 2962 deletions

View File

@ -19,19 +19,17 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController 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.searchactions.actions.SearchAction
import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.component.SearchBarLevel
import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM
import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler 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.SearchColumn
import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.search.SearchVM
import de.mm20.launcher2.ui.launcher.searchbar.LauncherSearchBar import de.mm20.launcher2.ui.launcher.searchbar.LauncherSearchBar
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
@Composable @Composable
fun AssistantScaffold( fun AssistantScaffold(
@ -204,7 +202,7 @@ fun AssistantScaffold(
showHiddenItemsButton = true, showHiddenItemsButton = true,
value = { value }, value = { value },
onValueChange = { searchVM.search(it) }, 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, style = searchBarStyle,
reverse = bottomSearchBar, reverse = bottomSearchBar,
onKeyboardActionGo = if (launchOnEnter) { onKeyboardActionGo = if (launchOnEnter) {

View File

@ -1,9 +1,11 @@
package de.mm20.launcher2.ui.base package de.mm20.launcher2.ui.base
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.unit.dp import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.Settings 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.component.ProvideIconShape
import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
@ -19,32 +21,28 @@ import org.koin.androidx.compose.inject
fun ProvideSettings( fun ProvideSettings(
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
val dataStore: LauncherDataStore by inject() val settings: UiSettings by inject()
val widgetRepository: WidgetRepository by inject() val widgetRepository: WidgetRepository by inject()
val cardStyle by remember { val cardStyle by remember {
dataStore.data.map { it.cards }.distinctUntilChanged() settings.cardStyle.distinctUntilChanged()
}.collectAsState( }.collectAsState(
Settings.CardSettings.getDefaultInstance() CardStyle()
) )
val iconShape by remember { val iconShape by remember {
dataStore.data.map { settings.iconShape.distinctUntilChanged()
if (it.easterEgg) Settings.IconSettings.IconShape.EasterEgg }.collectAsState(IconShape.Circle)
else it.icons.shape
}.distinctUntilChanged()
}.collectAsState(Settings.IconSettings.IconShape.Circle)
val favoritesEnabled by remember { val favoritesEnabled by remember {
combine( combine(
widgetRepository.exists(FavoritesWidget.Type), widgetRepository.exists(FavoritesWidget.Type),
dataStore.data.map { it.favorites.enabled }, settings.favoritesEnabled,
dataStore.data.map { it.clockWidget.favoritesPart }, ) { a, b -> a || b }.distinctUntilChanged()
) { a, b, c -> a || b || c }.distinctUntilChanged()
}.collectAsState(true) }.collectAsState(true)
val gridSettings by remember { val gridSettings by remember {
dataStore.data.map { it.grid }.distinctUntilChanged() settings.gridSettings.distinctUntilChanged()
}.collectAsState(Settings.GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build()) }.collectAsState(GridSettings())
CompositionLocalProvider( CompositionLocalProvider(
LocalCardStyle provides cardStyle, LocalCardStyle provides cardStyle,

View File

@ -4,7 +4,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
import de.mm20.launcher2.data.customattrs.utils.withCustomLabels 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.SavableSearchable
import de.mm20.launcher2.search.data.Tag import de.mm20.launcher2.search.data.Tag
import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.services.favorites.FavoritesService
@ -19,11 +20,11 @@ abstract class FavoritesVM : ViewModel(), KoinComponent {
private val favoritesService: FavoritesService by inject() private val favoritesService: FavoritesService by inject()
internal val widgetRepository: WidgetRepository by inject() internal val widgetRepository: WidgetRepository by inject()
private val customAttributesRepository: CustomAttributesRepository by inject() private val customAttributesRepository: CustomAttributesRepository by inject()
internal val dataStore: LauncherDataStore by inject() internal val settings: FavoritesSettings by inject()
val selectedTag = MutableStateFlow<String?>(null) val selectedTag = MutableStateFlow<String?>(null)
val showEditButton = dataStore.data.map { it.favorites.editButton } val showEditButton = settings.showEditButton
abstract val tagsExpanded: Flow<Boolean> abstract val tagsExpanded: Flow<Boolean>
val pinnedTags = favoritesService.getFavorites( val pinnedTags = favoritesService.getFavorites(
@ -36,24 +37,18 @@ abstract class FavoritesVM : ViewModel(), KoinComponent {
open val favorites: Flow<List<SavableSearchable>> = selectedTag.flatMapLatest { tag -> open val favorites: Flow<List<SavableSearchable>> = selectedTag.flatMapLatest { tag ->
if (tag == null) { if (tag == null) {
val columns = dataStore.data.map { it.grid.columnCount }
val excludeCalendar = widgetRepository.exists(CalendarWidget.Type) val excludeCalendar = widgetRepository.exists(CalendarWidget.Type)
val includeFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
combine( combine(
listOf( excludeCalendar,
columns, settings,
excludeCalendar, ) { (a, b) -> a as Boolean to b as FavoritesSettingsData }
includeFrequentlyUsed, .transformLatest {
frequentlyUsedRows
)
) { it }.transformLatest {
val columns = it[0] as Int val columns = it.second.columns
val excludeCalendar = it[1] as Boolean val excludeCalendar = it.first
val includeFrequentlyUsed = it[2] as Boolean val includeFrequentlyUsed = it.second.frequentlyUsed
val frequentlyUsedRows = it[3] as Int val frequentlyUsedRows = it.second.frequentlyUsedRows
val pinned = favoritesService.getFavorites( val pinned = favoritesService.getFavorites(
excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"), excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"),

View File

@ -5,7 +5,8 @@ import android.net.Uri
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.Theme
import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.ThemeRepository
import de.mm20.launcher2.themes.fromJson import de.mm20.launcher2.themes.fromJson
@ -17,7 +18,7 @@ import org.koin.core.component.inject
class ImportThemeSheetVM : ViewModel(), KoinComponent { class ImportThemeSheetVM : ViewModel(), KoinComponent {
private val themeRepository: ThemeRepository by inject() private val themeRepository: ThemeRepository by inject()
private val dataStore: LauncherDataStore by inject() private val uiSettings: UiSettings by inject()
val theme = mutableStateOf<Theme?>(null) val theme = mutableStateOf<Theme?>(null)
val error = mutableStateOf<Boolean>(false) val error = mutableStateOf<Boolean>(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) themeRepository.createTheme(theme)
if (apply) { if (apply) {
dataStore.updateData { uiSettings.setTheme(ThemeDescriptor.Custom(theme.id.toString()))
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setThemeId(theme.id.toString())
).build()
}
} }
} }
} }

View File

@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.SearchService
import de.mm20.launcher2.search.toList import de.mm20.launcher2.search.toList
@ -15,15 +14,13 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject 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 searchService: SearchService by inject()
private val iconService: IconService by inject() private val iconService: IconService by inject()
@ -41,15 +38,11 @@ class SearchablePickerVM: ViewModel(), KoinComponent {
searchQuery = query searchQuery = query
searchJob?.cancel() searchJob?.cancel()
searchJob = viewModelScope.launch { searchJob = viewModelScope.launch {
val settings = dataStore.data.first()
searchService.search( searchService.search(
query = query, query = query,
shortcuts = settings.appShortcutSearch,
contacts = settings.contactsSearch,
calendars = settings.calendarSearch,
).collectLatest { ).collectLatest {
if (searchQuery != query) return@collectLatest if (searchQuery != query) return@collectLatest
items = withContext(Dispatchers.Default) { items = withContext(Dispatchers.Default) {
it.toList().filterIsInstance<SavableSearchable>().sorted() it.toList().filterIsInstance<SavableSearchable>().sorted()
} }
} }

View File

@ -2,9 +2,9 @@ package de.mm20.launcher2.ui.common
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel 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.WeatherRepository
import de.mm20.launcher2.weather.settings.WeatherSettings
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent

View File

@ -39,7 +39,7 @@ import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp 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.R
import de.mm20.launcher2.ui.layout.BottomReversed import de.mm20.launcher2.ui.layout.BottomReversed
import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalCardStyle
@ -47,7 +47,7 @@ import de.mm20.launcher2.ui.locals.LocalCardStyle
@Composable @Composable
fun SearchBar( fun SearchBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: Settings.SearchBarSettings.SearchBarStyle, style: SearchBarStyle,
level: SearchBarLevel, level: SearchBarLevel,
value: String, value: String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,
@ -78,7 +78,7 @@ fun SearchBar(
} }
) { ) {
when { when {
it == SearchBarLevel.Resting && style != Settings.SearchBarSettings.SearchBarStyle.Solid -> 0.dp it == SearchBarLevel.Resting && style != SearchBarStyle.Solid -> 0.dp
it == SearchBarLevel.Raised -> 8.dp it == SearchBarLevel.Raised -> 8.dp
else -> 2.dp else -> 2.dp
} }
@ -98,7 +98,7 @@ fun SearchBar(
}) { }) {
when { when {
it == SearchBarLevel.Active -> LocalCardStyle.current.opacity it == SearchBarLevel.Active -> LocalCardStyle.current.opacity
style != Settings.SearchBarSettings.SearchBarStyle.Transparent -> 1f style != SearchBarStyle.Transparent -> 1f
it == SearchBarLevel.Resting -> 0f it == SearchBarLevel.Resting -> 0f
else -> 1f else -> 1f
} }
@ -117,14 +117,14 @@ fun SearchBar(
} }
}) { }) {
when { 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 it == SearchBarLevel.Resting -> if (darkColors) Color(0, 0, 0, 180) else Color.White
else -> MaterialTheme.colorScheme.onSurface else -> MaterialTheme.colorScheme.onSurface
} }
} }
val opacity by transition.animateFloat(label = "opacity") { 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 else 1f
} }

View File

@ -15,7 +15,6 @@ import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding 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.nativeCanvas
import androidx.compose.ui.graphics.toAndroidRect import androidx.compose.ui.graphics.toAndroidRect
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Density 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.TintedIconLayer
import de.mm20.launcher2.icons.TransparentLayer import de.mm20.launcher2.icons.TransparentLayer
import de.mm20.launcher2.ktx.drawWithColorFilter 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.base.LocalTime
import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalDarkTheme
@ -495,7 +493,6 @@ fun getShape(iconShape: IconShape): Shape {
IconShape.Teardrop -> TeardropShape IconShape.Teardrop -> TeardropShape
IconShape.Pebble -> PebbleShape IconShape.Pebble -> PebbleShape
IconShape.EasterEgg -> EasterEggShape IconShape.EasterEgg -> EasterEggShape
IconShape.UNRECOGNIZED -> CircleShape
} }
} }

View File

@ -12,17 +12,20 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.globalactions.GlobalActionsService import de.mm20.launcher2.globalactions.GlobalActionsService
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout 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.search.SavableSearchable
import de.mm20.launcher2.ui.gestures.Gesture import de.mm20.launcher2.ui.gestures.Gesture
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -32,7 +35,8 @@ import org.koin.core.component.inject
class LauncherScaffoldVM : ViewModel(), KoinComponent { 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 globalActionsService: GlobalActionsService by inject()
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val searchableRepository: SavableSearchableRepository by inject() private val searchableRepository: SavableSearchableRepository by inject()
@ -40,40 +44,41 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
private var isSystemInDarkMode = MutableStateFlow(false) private var isSystemInDarkMode = MutableStateFlow(false)
private val dimBackgroundState = combine( private val dimBackgroundState = combine(
dataStore.data.map { it.appearance.dimWallpaper }, uiSettings.dimWallpaper,
dataStore.data.map { it.appearance.theme }, uiSettings.colorScheme,
isSystemInDarkMode isSystemInDarkMode
) { dim, theme, systemDarkMode -> ) { 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 dimBackground = dimBackgroundState.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val statusBarColor = dataStore.data.map { it.systemBars.statusBarColor } val statusBarColor = uiSettings.statusBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val navBarColor = dataStore.data.map { it.systemBars.statusBarColor } val navBarColor = uiSettings.navigationBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val chargingAnimation = dataStore.data.map { it.animations.charging } val chargingAnimation = uiSettings.chargingAnimation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar } val hideNavBar = uiSettings.hideNavigationBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar } val hideStatusBar = uiSettings.hideStatusBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setSystemInDarkMode(darkMode: Boolean) { fun setSystemInDarkMode(darkMode: Boolean) {
isSystemInDarkMode.value = darkMode isSystemInDarkMode.value = darkMode
} }
val baseLayout = dataStore.data.map { it.layout.baseLayout } val baseLayout = uiSettings.baseLayout
.stateIn(viewModelScope, SharingStarted.Eagerly, null) .stateIn(viewModelScope, SharingStarted.Eagerly, null)
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar } val bottomSearchBar = uiSettings.bottomSearchBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults } val reverseSearchResults = uiSettings.reverseSearchResults
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar } val fixedSearchBar = uiSettings.fixedSearchBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
val isSearchOpen = mutableStateOf(false) val isSearchOpen = mutableStateOf(false)
@ -81,8 +86,7 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
val searchBarFocused = mutableStateOf(false) val searchBarFocused = mutableStateOf(false)
val autoFocusSearch = uiSettings.openKeyboardOnSearch
val autoFocusSearch = dataStore.data.map { it.searchBar.autoFocus }
fun setSearchbarFocus(focused: Boolean) { fun setSearchbarFocus(focused: Boolean) {
if (searchBarFocused.value != focused) searchBarFocused.value = focused if (searchBarFocused.value != focused) searchBarFocused.value = focused
@ -120,45 +124,43 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
isWidgetEditMode.value = editMode isWidgetEditMode.value = editMode
} }
val wallpaperBlur = dataStore.data.map { it.appearance.blurWallpaper } val wallpaperBlur = uiSettings.blurWallpaper
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
val wallpaperBlurRadius = dataStore.data.map { it.appearance.blurWallpaperRadius } val wallpaperBlurRadius = uiSettings.wallpaperBlurRadius
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32)
val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight } val fillClockHeight = uiSettings.clockFillScreen
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
val searchBarColor = dataStore.data.map { it.searchBar.color } val searchBarColor = uiSettings.searchBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarColors.Auto) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarColors.Auto)
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle } val searchBarStyle = uiSettings.searchBarStyle
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarStyle.Transparent) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarStyle.Transparent)
val gestureState: StateFlow<GestureState> = dataStore val gestureState: StateFlow<GestureState> = gestureSettings
.data.map { it.gestures }
.distinctUntilChanged()
.combine(baseLayout) { settings, layout -> .combine(baseLayout) { settings, layout ->
val swipeLeftAction = val swipeLeftAction =
settings?.swipeLeft?.takeIf { layout != Layout.Pager } ?: GestureAction.None settings.swipeLeft.takeIf { layout != BaseLayout.Pager } ?: GestureAction.NoAction
val swipeRightAction = settings?.swipeRight?.takeIf { layout != Layout.PagerReversed } val swipeRightAction = settings.swipeRight.takeIf { layout != BaseLayout.PagerReversed }
?: GestureAction.None ?: GestureAction.NoAction
val swipeDownAction = val swipeDownAction =
settings?.swipeDown?.takeIf { layout != Layout.PullDown } ?: GestureAction.None settings.swipeDown.takeIf { layout != BaseLayout.PullDown } ?: GestureAction.NoAction
val longPressAction = settings?.longPress ?: GestureAction.None val longPressAction = settings.longPress
val doubleTapAction = settings?.doubleTap ?: GestureAction.None val doubleTapAction = settings.doubleTap
val homeButtonAction = settings?.homeButton ?: GestureAction.None val homeButtonAction = settings.homeButton
val swipeLeftAppKey = val swipeLeftAppKey =
if (swipeLeftAction == GestureAction.LaunchApp) settings.swipeLeftApp else null if (swipeLeftAction is GestureAction.Launch) swipeLeftAction.key else null
val swipeRightAppKey = val swipeRightAppKey =
if (swipeRightAction == GestureAction.LaunchApp) settings.swipeRightApp else null if (swipeRightAction is GestureAction.Launch) swipeRightAction.key else null
val swipeDownAppKey = val swipeDownAppKey =
if (swipeDownAction == GestureAction.LaunchApp) settings.swipeDownApp else null if (swipeDownAction is GestureAction.Launch) swipeDownAction.key else null
val longPressAppKey = val longPressAppKey =
if (longPressAction == GestureAction.LaunchApp) settings.longPressApp else null if (longPressAction is GestureAction.Launch) longPressAction.key else null
val doubleTapAppKey = val doubleTapAppKey =
if (doubleTapAction == GestureAction.LaunchApp) settings.doubleTapApp else null if (doubleTapAction is GestureAction.Launch) doubleTapAction.key else null
val homeButtonAppKey = val homeButtonAppKey =
if (homeButtonAction == GestureAction.LaunchApp) settings.homeButtonApp else null if (homeButtonAction is GestureAction.Launch) homeButtonAction.key else null
val apps = listOfNotNull( val apps = listOfNotNull(
swipeLeftAppKey, swipeLeftAppKey,
swipeRightAppKey, swipeRightAppKey,
@ -189,17 +191,17 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
val action = when (gesture) { val action = when (gesture) {
Gesture.DoubleTap -> gestureState.value.doubleTapAction Gesture.DoubleTap -> gestureState.value.doubleTapAction
Gesture.LongPress -> gestureState.value.longPressAction Gesture.LongPress -> gestureState.value.longPressAction
Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != Layout.PullDown } Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != BaseLayout.PullDown }
Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != Layout.Pager } Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != BaseLayout.Pager }
Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != Layout.PagerReversed } Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != BaseLayout.PagerReversed }
Gesture.HomeButton -> gestureState.value.homeButtonAction Gesture.HomeButton -> gestureState.value.homeButtonAction
} }
val requiresAccessibilityService = val requiresAccessibilityService =
action == GestureAction.OpenRecents action is GestureAction.Recents
|| action == GestureAction.OpenPowerDialog || action is GestureAction.PowerMenu
|| action == GestureAction.OpenQuickSettings || action is GestureAction.QuickSettings
|| action == GestureAction.OpenNotificationDrawer || action is GestureAction.Notifications
|| action == GestureAction.LockScreen || action is GestureAction.ScreenLock
if (action != null && requiresAccessibilityService && !permissionsManager.checkPermissionOnce( if (action != null && requiresAccessibilityService && !permissionsManager.checkPermissionOnce(
PermissionGroup.Accessibility PermissionGroup.Accessibility
@ -211,37 +213,37 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
return when (action) { return when (action) {
GestureAction.OpenSearch -> { is GestureAction.Search -> {
openSearch() openSearch()
true true
} }
GestureAction.OpenNotificationDrawer -> { is GestureAction.Notifications -> {
globalActionsService.openNotificationDrawer() globalActionsService.openNotificationDrawer()
true true
} }
GestureAction.OpenQuickSettings -> { is GestureAction.QuickSettings -> {
globalActionsService.openQuickSettings() globalActionsService.openQuickSettings()
true true
} }
GestureAction.LockScreen -> { is GestureAction.ScreenLock -> {
globalActionsService.lockScreen() globalActionsService.lockScreen()
true true
} }
GestureAction.OpenPowerDialog -> { is GestureAction.PowerMenu -> {
globalActionsService.openPowerDialog() globalActionsService.openPowerDialog()
true true
} }
GestureAction.OpenRecents -> { is GestureAction.Recents -> {
globalActionsService.openRecents() globalActionsService.openRecents()
true true
} }
GestureAction.LaunchApp -> { is GestureAction.Launch -> {
val view = (context as Activity).window.decorView val view = (context as Activity).window.decorView
val options = ActivityOptionsCompat.makeScaleUpAnimation( val options = ActivityOptionsCompat.makeScaleUpAnimation(
view, view,
@ -271,12 +273,12 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
} }
data class GestureState( data class GestureState(
val swipeLeftAction: GestureAction = GestureAction.None, val swipeLeftAction: GestureAction = GestureAction.NoAction,
val swipeRightAction: GestureAction = GestureAction.None, val swipeRightAction: GestureAction = GestureAction.NoAction,
val swipeDownAction: GestureAction = GestureAction.None, val swipeDownAction: GestureAction = GestureAction.NoAction,
val longPressAction: GestureAction = GestureAction.None, val longPressAction: GestureAction = GestureAction.NoAction,
val doubleTapAction: GestureAction = GestureAction.None, val doubleTapAction: GestureAction = GestureAction.NoAction,
val homeButtonAction: GestureAction = GestureAction.None, val homeButtonAction: GestureAction = GestureAction.NoAction,
val swipeLeftApp: SavableSearchable? = null, val swipeLeftApp: SavableSearchable? = null,
val swipeRightApp: SavableSearchable? = null, val swipeRightApp: SavableSearchable? = null,
val swipeDownApp: SavableSearchable? = null, val swipeDownApp: SavableSearchable? = null,

View File

@ -86,7 +86,7 @@ import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController 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.searchactions.actions.SearchAction
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.component.SearchBarLevel

View File

@ -68,7 +68,7 @@ import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController 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.searchactions.actions.SearchAction
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.component.SearchBarLevel
@ -605,7 +605,7 @@ fun PullDownScaffold(
showHiddenItemsButton = isSearchOpen, showHiddenItemsButton = isSearchOpen,
value = { value }, value = { value },
onValueChange = { searchVM.search(it) }, 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, style = searchBarStyle,
reverse = bottomSearchBar, reverse = bottomSearchBar,
onKeyboardActionGo = if (launchOnEnter) { onKeyboardActionGo = if (launchOnEnter) {

View File

@ -34,8 +34,8 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.flowWithLifecycle
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.Settings.SystemBarsSettings.SystemBarColors import de.mm20.launcher2.preferences.SystemBarColors
import de.mm20.launcher2.ui.assistant.AssistantScaffold import de.mm20.launcher2.ui.assistant.AssistantScaffold
import de.mm20.launcher2.ui.base.BaseActivity import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.base.ProvideCurrentTime import de.mm20.launcher2.ui.base.ProvideCurrentTime
@ -184,7 +184,7 @@ abstract class SharedLauncherActivity(
} }
} else { } else {
when (layout) { when (layout) {
Settings.LayoutSettings.Layout.PullDown -> { BaseLayout.PullDown -> {
key(bottomSearchBar, reverseSearchResults) { key(bottomSearchBar, reverseSearchResults) {
PullDownScaffold( PullDownScaffold(
modifier = Modifier modifier = Modifier
@ -205,8 +205,8 @@ abstract class SharedLauncherActivity(
} }
} }
Settings.LayoutSettings.Layout.Pager, BaseLayout.Pager,
Settings.LayoutSettings.Layout.PagerReversed -> { BaseLayout.PagerReversed -> {
key(bottomSearchBar, reverseSearchResults) { key(bottomSearchBar, reverseSearchResults) {
PagerScaffold( PagerScaffold(
modifier = Modifier modifier = Modifier
@ -220,7 +220,7 @@ abstract class SharedLauncherActivity(
}, },
darkStatusBarIcons = lightStatus, darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav, darkNavBarIcons = lightNav,
reverse = layout == Settings.LayoutSettings.Layout.PagerReversed, reverse = layout == BaseLayout.PagerReversed,
bottomSearchBar = bottomSearchBar, bottomSearchBar = bottomSearchBar,
reverseSearchResults = reverseSearchResults, reverseSearchResults = reverseSearchResults,
fixedSearchBar = fixedSearchBar, fixedSearchBar = fixedSearchBar,

View File

@ -19,7 +19,7 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.search.SavableSearchable
import de.mm20.launcher2.ui.component.FakeSplashScreen import de.mm20.launcher2.ui.component.FakeSplashScreen
import de.mm20.launcher2.ui.gestures.Gesture import de.mm20.launcher2.ui.gestures.Gesture
@ -52,7 +52,7 @@ fun LauncherGestureHandler(
val gestureState by viewModel.gestureState.collectAsState(GestureState()) val gestureState by viewModel.gestureState.collectAsState(GestureState())
val shouldDetectDoubleTapGesture = gestureState.doubleTapAction != GestureAction.None val shouldDetectDoubleTapGesture = gestureState.doubleTapAction !is GestureAction.NoAction
LaunchedEffect(shouldDetectDoubleTapGesture) { LaunchedEffect(shouldDetectDoubleTapGesture) {
gestureDetector.shouldDetectDoubleTaps = shouldDetectDoubleTapGesture gestureDetector.shouldDetectDoubleTaps = shouldDetectDoubleTapGesture

View File

@ -5,25 +5,30 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.Ordering
import de.mm20.launcher2.preferences.Settings.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.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.Contact
import de.mm20.launcher2.search.File import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchService import de.mm20.launcher2.search.SearchService
import de.mm20.launcher2.search.Searchable 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.Website
import de.mm20.launcher2.search.data.Calculator import de.mm20.launcher2.search.data.Calculator
import de.mm20.launcher2.search.data.UnitConverter import de.mm20.launcher2.search.data.UnitConverter
import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.searchactions.actions.SearchAction
import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.services.favorites.FavoritesService
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -32,7 +37,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.shareIn
@ -47,10 +51,14 @@ class SearchVM : ViewModel(), KoinComponent {
private val favoritesService: FavoritesService by inject() private val favoritesService: FavoritesService by inject()
private val searchableRepository: SavableSearchableRepository by inject() private val searchableRepository: SavableSearchableRepository by inject()
private val permissionsManager: PermissionsManager 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) .stateIn(viewModelScope, SharingStarted.Eagerly, false)
private val searchService: SearchService by inject() private val searchService: SearchService by inject()
@ -70,10 +78,10 @@ class SearchVM : ViewModel(), KoinComponent {
val unitConverterResults = mutableStateOf<List<UnitConverter>>(emptyList()) val unitConverterResults = mutableStateOf<List<UnitConverter>>(emptyList())
val searchActionResults = mutableStateOf<List<SearchAction>>(emptyList()) val searchActionResults = mutableStateOf<List<SearchAction>>(emptyList())
val hiddenResultsButton = dataStore.data.map { it.searchBar.hiddenItemsButton } val hiddenResultsButton = searchUiSettings.hiddenItemsButton
val hiddenResults = mutableStateOf<List<SavableSearchable>>(emptyList()) val hiddenResults = mutableStateOf<List<SavableSearchable>>(emptyList())
val favoritesEnabled = dataStore.data.map { it.favorites.enabled } val favoritesEnabled = searchUiSettings.favorites
val hideFavorites = mutableStateOf(false) val hideFavorites = mutableStateOf(false)
private val hiddenItemKeys = searchableRepository private val hiddenItemKeys = searchableRepository
@ -117,16 +125,9 @@ class SearchVM : ViewModel(), KoinComponent {
} }
hideFavorites.value = query.isNotEmpty() hideFavorites.value = query.isNotEmpty()
searchJob = viewModelScope.launch { searchJob = viewModelScope.launch {
dataStore.data.collectLatest { settings -> searchUiSettings.resultOrder.collectLatest { resultOrder ->
searchService.search( searchService.search(
query, query
calculator = settings.calculatorSearch,
unitConverter = settings.unitConverterSearch,
calendars = settings.calendarSearch,
contacts = settings.contactsSearch,
shortcuts = settings.appShortcutSearch,
websites = settings.websiteSearch,
wikipedia = settings.wikipediaSearch,
).collectLatest { results -> ).collectLatest { results ->
var resultsList = withContext(Dispatchers.Default) { var resultsList = withContext(Dispatchers.Default) {
listOfNotNull( listOfNotNull(
@ -151,13 +152,13 @@ class SearchVM : ViewModel(), KoinComponent {
emptyList() emptyList()
} else { } else {
val keys = resultsList.mapNotNull { (it as? SavableSearchable)?.key } val keys = resultsList.mapNotNull { (it as? SavableSearchable)?.key }
when (settings.resultOrdering.ordering) { when (resultOrder) {
Ordering.LaunchCount -> searchableRepository.sortByRelevance( SearchResultOrder.LaunchCount -> searchableRepository.sortByRelevance(
keys keys
).first() ).first()
Ordering.Weighted -> searchableRepository.sortByWeight( SearchResultOrder.Weighted -> searchableRepository.sortByWeight(
keys keys
).first() ).first()
@ -256,7 +257,7 @@ class SearchVM : ViewModel(), KoinComponent {
val missingCalendarPermission = combine( val missingCalendarPermission = combine(
permissionsManager.hasPermission(PermissionGroup.Calendar), permissionsManager.hasPermission(PermissionGroup.Calendar),
dataStore.data.map { it.calendarSearch.enabled }.distinctUntilChanged() calendarSearchSettings.enabled,
) { perm, enabled -> !perm && enabled } ) { perm, enabled -> !perm && enabled }
fun requestCalendarPermission(context: AppCompatActivity) { fun requestCalendarPermission(context: AppCompatActivity) {
@ -264,18 +265,12 @@ class SearchVM : ViewModel(), KoinComponent {
} }
fun disableCalendarSearch() { fun disableCalendarSearch() {
viewModelScope.launch { calendarSearchSettings.setEnabled(false)
dataStore.updateData {
it.toBuilder()
.setCalendarSearch(it.calendarSearch.toBuilder().setEnabled(false))
.build()
}
}
} }
val missingContactsPermission = combine( val missingContactsPermission = combine(
permissionsManager.hasPermission(PermissionGroup.Contacts), permissionsManager.hasPermission(PermissionGroup.Contacts),
dataStore.data.map { it.contactsSearch.enabled }.distinctUntilChanged() contactSearchSettings.enabled
) { perm, enabled -> !perm && enabled } ) { perm, enabled -> !perm && enabled }
fun requestContactsPermission(context: AppCompatActivity) { fun requestContactsPermission(context: AppCompatActivity) {
@ -283,18 +278,12 @@ class SearchVM : ViewModel(), KoinComponent {
} }
fun disableContactsSearch() { fun disableContactsSearch() {
viewModelScope.launch { contactSearchSettings.setEnabled(false)
dataStore.updateData {
it.toBuilder()
.setContactsSearch(it.contactsSearch.toBuilder().setEnabled(false))
.build()
}
}
} }
val missingFilesPermission = combine( val missingFilesPermission = combine(
permissionsManager.hasPermission(PermissionGroup.ExternalStorage), permissionsManager.hasPermission(PermissionGroup.ExternalStorage),
fileSearchSettings.localFiles.distinctUntilChanged() fileSearchSettings.localFiles
) { perm, enabled -> !perm && enabled } ) { perm, enabled -> !perm && enabled }
fun requestFilesPermission(context: AppCompatActivity) { fun requestFilesPermission(context: AppCompatActivity) {
@ -302,18 +291,12 @@ class SearchVM : ViewModel(), KoinComponent {
} }
fun disableFilesSearch() { fun disableFilesSearch() {
viewModelScope.launch { fileSearchSettings.setLocalFiles(false)
dataStore.updateData {
it.toBuilder()
.setFileSearch(it.fileSearch.toBuilder().setLocalFiles(false))
.build()
}
}
} }
val missingAppShortcutPermission = combine( val missingAppShortcutPermission = combine(
permissionsManager.hasPermission(PermissionGroup.AppShortcuts), permissionsManager.hasPermission(PermissionGroup.AppShortcuts),
dataStore.data.map { it.appShortcutSearch.enabled }.distinctUntilChanged() shortcutSearchSettings.enabled,
) { perm, enabled -> !perm && enabled } ) { perm, enabled -> !perm && enabled }
fun requestAppShortcutPermission(context: AppCompatActivity) { fun requestAppShortcutPermission(context: AppCompatActivity) {
@ -321,12 +304,6 @@ class SearchVM : ViewModel(), KoinComponent {
} }
fun disableAppShortcutSearch() { fun disableAppShortcutSearch() {
viewModelScope.launch { shortcutSearchSettings.setEnabled(false)
dataStore.updateData {
it.toBuilder()
.setAppShortcutSearch(it.appShortcutSearch.toBuilder().setEnabled(false))
.build()
}
}
} }
} }

View File

@ -1,29 +1,23 @@
package de.mm20.launcher2.ui.launcher.search.favorites package de.mm20.launcher2.ui.launcher.search.favorites
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.ui.UiState
import de.mm20.launcher2.ui.common.FavoritesVM import de.mm20.launcher2.ui.common.FavoritesVM
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.component.inject
class SearchFavoritesVM : FavoritesVM() { class SearchFavoritesVM : FavoritesVM() {
override val tagsExpanded: Flow<Boolean> = dataStore.data.map { it.ui.searchTagsMultiline } private val uiState: UiState by inject()
override val tagsExpanded: Flow<Boolean> = uiState.favoritesTagsExpanded
.shareIn(viewModelScope, SharingStarted.Lazily) .shareIn(viewModelScope, SharingStarted.Lazily)
override fun setTagsExpanded(expanded: Boolean) { override fun setTagsExpanded(expanded: Boolean) {
viewModelScope.launch { uiState.setFavoritesTagsExpanded(expanded)
dataStore.updateData {
it.toBuilder()
.setUi(
it.ui.toBuilder()
.setSearchTagsMultiline(expanded)
.build()
)
.build()
}
}
} }
} }

View File

@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalFocusManager
import androidx.lifecycle.viewmodel.compose.viewModel 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.searchactions.actions.SearchAction
import de.mm20.launcher2.ui.component.SearchBar import de.mm20.launcher2.ui.component.SearchBar
import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.component.SearchBarLevel
@ -29,7 +29,7 @@ import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
@Composable @Composable
fun LauncherSearchBar( fun LauncherSearchBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
style: Settings.SearchBarSettings.SearchBarStyle, style: SearchBarStyle,
level: () -> SearchBarLevel, level: () -> SearchBarLevel,
value: () -> String, value: () -> String,
onValueChange: (String) -> Unit, onValueChange: (String) -> Unit,

View File

@ -17,9 +17,9 @@ import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.normalize import de.mm20.launcher2.ktx.normalize
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.appshortcuts.AppShortcut import de.mm20.launcher2.appshortcuts.AppShortcut
import de.mm20.launcher2.preferences.search.FavoritesSettings
import de.mm20.launcher2.search.Searchable import de.mm20.launcher2.search.Searchable
import de.mm20.launcher2.search.data.Tag import de.mm20.launcher2.search.data.Tag
import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.services.favorites.FavoritesService
@ -41,7 +41,7 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
private val badgeService: BadgeService by inject() private val badgeService: BadgeService by inject()
private val customAttributesRepository: CustomAttributesRepository by inject() private val customAttributesRepository: CustomAttributesRepository by inject()
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val dataStore: LauncherDataStore by inject() private val favoritesSettings: FavoritesSettings by inject()
val gridItems = mutableStateOf<List<FavoritesSheetGridItem>>(emptyList()) val gridItems = mutableStateOf<List<FavoritesSheetGridItem>>(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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFrequentlyUsed(frequentlyUsed: Boolean) { fun setFrequentlyUsed(frequentlyUsed: Boolean) {
viewModelScope.launch { favoritesSettings.setFrequentlyUsed(frequentlyUsed)
dataStore.updateData {
it.toBuilder()
.setFavorites(
it.favorites
.toBuilder()
.setFrequentlyUsed(frequentlyUsed)
)
.build()
}
}
} }
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows } val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) { fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
viewModelScope.launch { favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows)
dataStore.updateData {
it.toBuilder()
.setFavorites(
it.favorites
.toBuilder()
.setFrequentlyUsedRows(frequentlyUsedRows)
)
.build()
}
}
} }
fun pinTag(tag: Tag) { fun pinTag(tag: Tag) {

View File

@ -14,7 +14,8 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.R
import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.MissingPermissionBanner
@ -29,12 +30,12 @@ fun FailedGestureSheet(
val viewModel: FailedGestureSheetVM = viewModel() val viewModel: FailedGestureSheetVM = viewModel()
val actionName = stringResource(when(failedGesture.action) { val actionName = stringResource(when(failedGesture.action) {
Settings.GestureSettings.GestureAction.OpenSearch -> R.string.gesture_action_open_search is GestureAction.Search -> R.string.gesture_action_open_search
Settings.GestureSettings.GestureAction.OpenNotificationDrawer -> R.string.gesture_action_notifications is GestureAction.Notifications -> R.string.gesture_action_notifications
Settings.GestureSettings.GestureAction.LockScreen -> R.string.gesture_action_lock_screen is GestureAction.ScreenLock -> R.string.gesture_action_lock_screen
Settings.GestureSettings.GestureAction.OpenQuickSettings -> R.string.gesture_action_quick_settings is GestureAction.QuickSettings -> R.string.gesture_action_quick_settings
Settings.GestureSettings.GestureAction.OpenRecents -> R.string.gesture_action_recents is GestureAction.Recents -> R.string.gesture_action_recents
Settings.GestureSettings.GestureAction.OpenPowerDialog -> R.string.gesture_action_power_menu is GestureAction.PowerMenu -> R.string.gesture_action_power_menu
else -> R.string.gesture_action_none else -> R.string.gesture_action_none
}) })
val gestureName = stringResource(when(failedGesture.gesture) { val gestureName = stringResource(when(failedGesture.gesture) {

View File

@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction import de.mm20.launcher2.preferences.ui.GestureSettings
import de.mm20.launcher2.ui.gestures.Gesture import de.mm20.launcher2.ui.gestures.Gesture
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
@ -14,28 +14,20 @@ import org.koin.core.component.inject
class FailedGestureSheetVM : ViewModel(), KoinComponent { class FailedGestureSheetVM : ViewModel(), KoinComponent {
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val dataStore: LauncherDataStore by inject() private val gestureSettings: GestureSettings by inject()
fun requestPermission(context: AppCompatActivity) { fun requestPermission(context: AppCompatActivity) {
permissionsManager.requestPermission(context, PermissionGroup.Accessibility) permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
} }
fun disableGesture(gesture: Gesture) { fun disableGesture(gesture: Gesture) {
viewModelScope.launch { when(gesture) {
dataStore.updateData { Gesture.DoubleTap -> gestureSettings.setDoubleTap(GestureAction.NoAction)
it.toBuilder().setGestures( Gesture.LongPress -> gestureSettings.setLongPress(GestureAction.NoAction)
it.gestures.toBuilder().apply { Gesture.SwipeDown -> gestureSettings.setSwipeDown(GestureAction.NoAction)
when (gesture) { Gesture.SwipeLeft -> gestureSettings.setSwipeLeft(GestureAction.NoAction)
Gesture.SwipeDown -> swipeDown = GestureAction.None Gesture.SwipeRight -> gestureSettings.setSwipeRight(GestureAction.NoAction)
Gesture.SwipeLeft -> swipeLeft = GestureAction.None Gesture.HomeButton -> gestureSettings.setHomeButton(GestureAction.NoAction)
Gesture.SwipeRight -> swipeRight = GestureAction.None
Gesture.DoubleTap -> doubleTap = GestureAction.None
Gesture.LongPress -> longPress = GestureAction.None
Gesture.HomeButton -> homeButton = GestureAction.None
}
}.build()
).build()
}
} }
} }
} }

View File

@ -1,9 +1,8 @@
package de.mm20.launcher2.ui.launcher.widgets package de.mm20.launcher2.ui.launcher.widgets
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.Widget
import de.mm20.launcher2.widgets.WidgetRepository import de.mm20.launcher2.widgets.WidgetRepository
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -15,9 +14,9 @@ import org.koin.core.component.inject
class WidgetsVM : ViewModel(), KoinComponent { class WidgetsVM : ViewModel(), KoinComponent {
private val widgetRepository: WidgetRepository by inject() 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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val widgets = widgetRepository.get() val widgets = widgetRepository.get()

View File

@ -14,8 +14,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width 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.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetAlignment import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.base.LocalTime import de.mm20.launcher2.ui.base.LocalTime
import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.BottomSheetDialog
@ -89,7 +86,7 @@ fun ClockWidget(
) { ) {
val viewModel: ClockWidgetVM = viewModel() val viewModel: ClockWidgetVM = viewModel()
val context = LocalContext.current val context = LocalContext.current
val layout by viewModel.layout.collectAsState() val compact by viewModel.compactLayout.collectAsState()
val clockStyle by viewModel.clockStyle.collectAsState() val clockStyle by viewModel.clockStyle.collectAsState()
val color by viewModel.color.collectAsState() val color by viewModel.color.collectAsState()
val alignment by viewModel.alignment.collectAsState() val alignment by viewModel.alignment.collectAsState()
@ -106,7 +103,9 @@ fun ClockWidget(
viewModel.updateTime(time) 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") { AnimatedContent(editMode, label = "ClockWidget") {
if (it) { if (it) {
@ -165,32 +164,32 @@ fun ClockWidget(
CompositionLocalProvider( CompositionLocalProvider(
LocalContentColor provides contentColor LocalContentColor provides contentColor
) { ) {
if (layout == ClockWidgetLayout.Vertical) { if (compact == false) {
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Box( Box(
modifier = Modifier.clickable( modifier = Modifier.clickable(
enabled = clockStyle != ClockStyle.EmptyClock, enabled = clockStyle !is ClockWidgetStyle.Empty,
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
viewModel.launchClockApp(context) viewModel.launchClockApp(context)
} }
) { ) {
Clock(clockStyle, ClockWidgetLayout.Vertical) Clock(clockStyle, false)
} }
if (partProvider != null) { if (partProvider != null) {
DynamicZone( DynamicZone(
modifier = Modifier.padding(bottom = 8.dp), modifier = Modifier.padding(bottom = 8.dp),
layout = ClockWidgetLayout.Vertical, compact = false,
provider = partProvider, provider = partProvider,
) )
} }
} }
} }
if (layout == ClockWidgetLayout.Horizontal) { if (compact == true) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -201,7 +200,7 @@ fun ClockWidget(
if (partProvider != null) { if (partProvider != null) {
DynamicZone( DynamicZone(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
layout = ClockWidgetLayout.Horizontal, compact = true,
provider = partProvider, provider = partProvider,
) )
} }
@ -216,14 +215,14 @@ fun ClockWidget(
) )
Box( Box(
modifier = Modifier.clickable( modifier = Modifier.clickable(
enabled = clockStyle != ClockStyle.EmptyClock, enabled = clockStyle !is ClockWidgetStyle.Empty,
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
viewModel.launchClockApp(context) viewModel.launchClockApp(context)
} }
) { ) {
Clock(clockStyle, ClockWidgetLayout.Horizontal) Clock(clockStyle, true)
} }
} }
} }
@ -236,7 +235,7 @@ fun ClockWidget(
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = 16.dp) .padding(bottom = 16.dp)
) { ) {
dockProvider?.Component(ClockWidgetLayout.Vertical) dockProvider?.Component(false)
} }
} }
} }
@ -246,21 +245,18 @@ fun ClockWidget(
@Composable @Composable
fun Clock( fun Clock(
style: ClockStyle?, style: ClockWidgetStyle?,
layout: ClockWidgetLayout compact: Boolean,
) { ) {
val time = LocalTime.current val time = LocalTime.current
when (style) { when (style) {
ClockStyle.DigitalClock1, is ClockWidgetStyle.Digital1 -> DigitalClock1(time, compact, style)
ClockStyle.DigitalClock1_Outlined,
ClockStyle.DigitalClock1_MDY,
ClockStyle.DigitalClock1_OnePlus -> DigitalClock1(time, layout, style)
ClockStyle.DigitalClock2 -> DigitalClock2(time, layout) is ClockWidgetStyle.Digital2 -> DigitalClock2(time, compact)
ClockStyle.BinaryClock -> BinaryClock(time, layout) is ClockWidgetStyle.Binary -> BinaryClock(time, compact)
ClockStyle.AnalogClock -> AnalogClock(time, layout) is ClockWidgetStyle.Analog -> AnalogClock(time, compact)
ClockStyle.OrbitClock -> OrbitClock(time, layout) is ClockWidgetStyle.Orbit -> OrbitClock(time, compact)
ClockStyle.EmptyClock -> {} is ClockWidgetStyle.Empty -> {}
else -> {} else -> {}
} }
} }
@ -268,13 +264,13 @@ fun Clock(
@Composable @Composable
fun DynamicZone( fun DynamicZone(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
layout: ClockWidgetLayout, compact: Boolean,
provider: PartProvider?, provider: PartProvider?,
) { ) {
Column( Column(
modifier = modifier modifier = modifier
) { ) {
provider?.Component(layout) provider?.Component(compact)
} }
} }
@ -283,18 +279,13 @@ fun ConfigureClockWidgetSheet(
onDismiss: () -> Unit, onDismiss: () -> Unit,
) { ) {
val viewModel: ClockWidgetSettingsScreenVM = viewModel() val viewModel: ClockWidgetSettingsScreenVM = viewModel()
val layout by viewModel.layout.collectAsState() val compact by viewModel.compact.collectAsState()
val color by viewModel.color.collectAsState() val color by viewModel.color.collectAsState()
val style by viewModel.clockStyle.collectAsState() val style by viewModel.clockStyle.collectAsState()
val fillHeight by viewModel.fillHeight.collectAsState() val fillHeight by viewModel.fillHeight.collectAsState()
val alignment by viewModel.alignment.collectAsState() val alignment by viewModel.alignment.collectAsState()
val dock by viewModel.dock.collectAsState()
val date by viewModel.datePart.collectAsState() val parts by viewModel.parts.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()
BottomSheetDialog(onDismissRequest = onDismiss) { BottomSheetDialog(onDismissRequest = onDismiss) {
Column( Column(
@ -307,14 +298,14 @@ fun ConfigureClockWidgetSheet(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { ) {
SegmentedButton( SegmentedButton(
selected = layout == ClockWidgetLayout.Vertical, selected = compact == false,
onClick = { onClick = {
viewModel.setLayout(ClockWidgetLayout.Vertical) viewModel.setCompact(false)
}, },
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2), shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
icon = { icon = {
SegmentedButtonDefaults.Icon( SegmentedButtonDefaults.Icon(
active = layout == ClockWidgetLayout.Vertical, active = compact == false,
) { ) {
Icon( Icon(
imageVector = Icons.Rounded.HorizontalSplit, imageVector = Icons.Rounded.HorizontalSplit,
@ -327,14 +318,14 @@ fun ConfigureClockWidgetSheet(
Text(text = stringResource(R.string.preference_clockwidget_layout_vertical)) Text(text = stringResource(R.string.preference_clockwidget_layout_vertical))
} }
SegmentedButton( SegmentedButton(
selected = layout == ClockWidgetLayout.Horizontal, selected = compact == true,
onClick = { onClick = {
viewModel.setLayout(ClockWidgetLayout.Horizontal) viewModel.setCompact(true)
}, },
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2), shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
icon = { icon = {
SegmentedButtonDefaults.Icon( SegmentedButtonDefaults.Icon(
active = layout == ClockWidgetLayout.Horizontal, active = compact == true,
) { ) {
Icon( Icon(
imageVector = Icons.Rounded.VerticalSplit, imageVector = Icons.Rounded.VerticalSplit,
@ -348,9 +339,9 @@ fun ConfigureClockWidgetSheet(
} }
} }
if (color != null && layout != null) { if (color != null && compact != null) {
WatchFaceSelector( WatchFaceSelector(
layout = layout!!, compact = compact!!,
colors = color!!, colors = color!!,
selected = style, selected = style,
onSelect = { onSelect = {
@ -487,7 +478,7 @@ fun ConfigureClockWidgetSheet(
title = stringResource(R.string.preference_clockwidget_favorites_part), title = stringResource(R.string.preference_clockwidget_favorites_part),
summary = stringResource(R.string.preference_clockwidget_favorites_part_summary), summary = stringResource(R.string.preference_clockwidget_favorites_part_summary),
icon = Icons.Rounded.Star, icon = Icons.Rounded.Star,
value = favorites == true, value = dock == true,
onValueChanged = { onValueChanged = {
viewModel.setFavoritesPart(it) viewModel.setFavoritesPart(it)
} }
@ -510,7 +501,7 @@ fun ConfigureClockWidgetSheet(
title = stringResource(R.string.preference_clockwidget_date_part), title = stringResource(R.string.preference_clockwidget_date_part),
summary = stringResource(R.string.preference_clockwidget_date_part_summary), summary = stringResource(R.string.preference_clockwidget_date_part_summary),
icon = Icons.Rounded.Today, icon = Icons.Rounded.Today,
value = date == true, value = parts?.date == true,
onValueChanged = { onValueChanged = {
viewModel.setDatePart(it) viewModel.setDatePart(it)
} }
@ -519,7 +510,7 @@ fun ConfigureClockWidgetSheet(
title = stringResource(R.string.preference_clockwidget_music_part), title = stringResource(R.string.preference_clockwidget_music_part),
summary = stringResource(R.string.preference_clockwidget_music_part_summary), summary = stringResource(R.string.preference_clockwidget_music_part_summary),
icon = Icons.Rounded.MusicNote, icon = Icons.Rounded.MusicNote,
value = media == true, value = parts?.music == true,
onValueChanged = { onValueChanged = {
viewModel.setMusicPart(it) viewModel.setMusicPart(it)
} }
@ -528,7 +519,7 @@ fun ConfigureClockWidgetSheet(
title = stringResource(R.string.preference_clockwidget_alarm_part), title = stringResource(R.string.preference_clockwidget_alarm_part),
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary), summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
icon = Icons.Rounded.Alarm, icon = Icons.Rounded.Alarm,
value = alarm == true, value = parts?.alarm == true,
onValueChanged = { onValueChanged = {
viewModel.setAlarmPart(it) viewModel.setAlarmPart(it)
} }
@ -537,7 +528,7 @@ fun ConfigureClockWidgetSheet(
title = stringResource(R.string.preference_clockwidget_battery_part), title = stringResource(R.string.preference_clockwidget_battery_part),
summary = stringResource(R.string.preference_clockwidget_battery_part_summary), summary = stringResource(R.string.preference_clockwidget_battery_part_summary),
icon = Icons.Rounded.BatteryFull, icon = Icons.Rounded.BatteryFull,
value = battery == true, value = parts?.battery == true,
onValueChanged = { onValueChanged = {
viewModel.setBatteryPart(it) viewModel.setBatteryPart(it)
} }

View File

@ -6,7 +6,7 @@ import android.provider.AlarmClock
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.ktx.tryStartActivity 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.AlarmPartProvider
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.BatteryPartProvider import de.mm20.launcher2.ui.launcher.widgets.clock.parts.BatteryPartProvider
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider 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.channelFlow
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class ClockWidgetVM : ViewModel(), KoinComponent { 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<PartProvider>() val providers = mutableListOf<PartProvider>()
if (it.datePart) providers += DatePartProvider() if (it.date) providers += DatePartProvider()
if (it.musicPart) providers += MusicPartProvider() if (it.music) providers += MusicPartProvider()
if (it.batteryPart) providers += BatteryPartProvider() if (it.battery) providers += BatteryPartProvider()
if (it.alarmPart) providers += AlarmPartProvider() if (it.alarm) providers += AlarmPartProvider()
providers providers
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) }.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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle } val clockStyle = settings.clockStyle
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val color = dataStore.data.map { it.clockWidget.color } val color = settings.color
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val alignment = dataStore.data.map { it.clockWidget.alignment } val alignment = settings.alignment
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val dockProvider = dataStore.data val dockProvider = settings.dock
.map { if (it.clockWidget.favoritesPart) FavoritesPartProvider() else null } .map { if (it) FavoritesPartProvider() else null }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun updateTime(time: Long) { fun updateTime(time: Long) {

View File

@ -43,19 +43,18 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun WatchFaceSelector( fun WatchFaceSelector(
layout: ClockWidgetLayout, compact: Boolean,
colors: ClockWidgetColors, colors: ClockWidgetColors,
selected: ClockStyle?, selected: ClockWidgetStyle?,
onSelect: (ClockStyle) -> Unit, onSelect: (ClockWidgetStyle) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
Surface( Surface(
@ -73,7 +72,7 @@ fun WatchFaceSelector(
modifier = Modifier, modifier = Modifier,
) { ) {
val styles = remember { val styles = remember {
sortedMapOf( mapOf(
ClockStyle.DigitalClock1 to 0, ClockStyle.DigitalClock1 to 0,
ClockStyle.DigitalClock1_Outlined to 0, ClockStyle.DigitalClock1_Outlined to 0,
ClockStyle.DigitalClock1_MDY to 0, ClockStyle.DigitalClock1_MDY to 0,
@ -159,9 +158,9 @@ fun WatchFaceSelector(
styles.filter { it.value == pageIndex } styles.filter { it.value == pageIndex }
} }
if (currentPageStyles.containsKey(selected)) { if (currentPageStyles.containsKey(selected)) {
Clock(selected, layout) Clock(selected, compact)
} else { } 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) { return when (style) {
ClockStyle.DigitalClock1, ClockStyle.DigitalClock1,
ClockStyle.DigitalClock1_Outlined, ClockStyle.DigitalClock1_Outlined,
@ -264,11 +263,11 @@ fun getClockstyleName(context: Context, style: ClockStyle): String {
ClockStyle.BinaryClock -> "Binary" ClockStyle.BinaryClock -> "Binary"
ClockStyle.AnalogClock -> "Hands" ClockStyle.AnalogClock -> "Hands"
ClockStyle.EmptyClock -> "Empty" ClockStyle.EmptyClock -> "Empty"
ClockStyle.UNRECOGNIZED -> "" else -> ""
} }
} }
fun getVariantName(context: Context, style: ClockStyle): String { fun getVariantName(context: Context, style: ClockWidgetStyle): String {
return when (style) { return when (style) {
ClockStyle.DigitalClock1, ClockStyle.DigitalClock1,
ClockStyle.DigitalClock2, ClockStyle.DigitalClock2,
@ -279,7 +278,20 @@ fun getVariantName(context: Context, style: ClockStyle): String {
ClockStyle.DigitalClock1_Outlined -> "Outlined" ClockStyle.DigitalClock1_Outlined -> "Outlined"
ClockStyle.DigitalClock1_MDY -> "Material You" ClockStyle.DigitalClock1_MDY -> "Material You"
ClockStyle.DigitalClock1_OnePlus -> "OnePlus" 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
}

View File

@ -11,15 +11,15 @@ import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Fill import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.graphics.drawscope.rotate
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import java.util.* import java.util.*
@Composable @Composable
fun AnalogClock( fun AnalogClock(
time: Long, time: Long,
layout: Settings.ClockWidgetSettings.ClockWidgetLayout compact: Boolean,
) { ) {
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical val verticalLayout = !compact
val date = Calendar.getInstance() val date = Calendar.getInstance()
date.timeInMillis = time date.timeInMillis = time
val minute = date[Calendar.MINUTE] val minute = date[Calendar.MINUTE]

View File

@ -7,15 +7,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.mm20.launcher2.preferences.Settings
import java.util.* import java.util.*
@Composable @Composable
fun BinaryClock( fun BinaryClock(
time: Long, time: Long,
layout: Settings.ClockWidgetSettings.ClockWidgetLayout compact: Boolean,
) { ) {
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical val verticalLayout = !compact
val date = Calendar.getInstance() val date = Calendar.getInstance()
date.timeInMillis = time date.timeInMillis = time
val minute = date[Calendar.MINUTE] val minute = date[Calendar.MINUTE]

View File

@ -8,7 +8,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color 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.Fill
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.platform.LocalContext 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.dp
import androidx.compose.ui.unit.em import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.ktx.toPixels
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalDarkTheme
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -30,10 +28,10 @@ import java.util.*
@Composable @Composable
fun DigitalClock1( fun DigitalClock1(
time: Long, time: Long,
layout: Settings.ClockWidgetSettings.ClockWidgetLayout, compact: Boolean,
variant: ClockStyle = ClockStyle.DigitalClock1, style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(),
) { ) {
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical val verticalLayout = !compact
val format = SimpleDateFormat( val format = SimpleDateFormat(
if (verticalLayout) { if (verticalLayout) {
if (DateFormat.is24HourFormat(LocalContext.current)) "HH\nmm" else "hh\nmm" if (DateFormat.is24HourFormat(LocalContext.current)) "HH\nmm" else "hh\nmm"
@ -50,8 +48,8 @@ fun DigitalClock1(
fontWeight = FontWeight.Black, fontWeight = FontWeight.Black,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
lineHeight = 0.8.em, lineHeight = 0.8.em,
drawStyle = if (variant == ClockStyle.DigitalClock1_Outlined) Stroke(width = 2.dp.toPixels()) else Fill, drawStyle = if (style.outlined) Stroke(width = 2.dp.toPixels()) else Fill,
color = if (variant == ClockStyle.DigitalClock1_MDY) { color = if (style.variant == ClockWidgetStyle.Digital1.Variant.MDY) {
if (LocalContentColor.current == Color.White) { if (LocalContentColor.current == Color.White) {
if (LocalDarkTheme.current) MaterialTheme.colorScheme.onPrimaryContainer if (LocalDarkTheme.current) MaterialTheme.colorScheme.onPrimaryContainer
else MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.primaryContainer
@ -64,7 +62,7 @@ fun DigitalClock1(
val modifier = Modifier.offset(0.dp, if (verticalLayout) 16.dp else 0.dp) 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) val hour = formattedString.substring(0, 2)
Text( Text(
modifier = modifier, modifier = modifier,

View File

@ -6,13 +6,12 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import de.mm20.launcher2.preferences.Settings
@Composable @Composable
fun DigitalClock2( fun DigitalClock2(
time: Long, time: Long,
layout: Settings.ClockWidgetSettings.ClockWidgetLayout compact: Boolean,
) { ) {
Text( Text(
text = DateUtils.formatDateTime(LocalContext.current, time, DateUtils.FORMAT_SHOW_TIME), text = DateUtils.formatDateTime(LocalContext.current, time, DateUtils.FORMAT_SHOW_TIME),

View File

@ -31,7 +31,7 @@ import androidx.compose.ui.unit.center
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toOffset
import de.mm20.launcher2.ktx.TWO_PI 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.Instant
import java.time.ZoneId import java.time.ZoneId
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -46,9 +46,9 @@ private val currentTime
@Composable @Composable
fun OrbitClock( fun OrbitClock(
_time: Long, _time: Long,
layout: Settings.ClockWidgetSettings.ClockWidgetLayout compact: Boolean,
) { ) {
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical val verticalLayout = !compact
val timeState = remember { mutableStateOf<ZonedDateTime>(currentTime) } val timeState = remember { mutableStateOf<ZonedDateTime>(currentTime) }

View File

@ -21,7 +21,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
@ -71,7 +70,7 @@ class AlarmPartProvider : PartProvider {
} }
@Composable @Composable
override fun Component(layout: ClockWidgetLayout) { override fun Component(compactLayout: Boolean) {
val context = LocalContext.current val context = LocalContext.current
val alarmTime by nextAlarmTime val alarmTime by nextAlarmTime
@ -79,7 +78,7 @@ class AlarmPartProvider : PartProvider {
alarmTime?.let { alarmTime?.let {
if (layout == ClockWidgetLayout.Vertical) { if (!compactLayout) {
TextButton( TextButton(
onClick = { onClick = {

View File

@ -20,14 +20,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.icons.* import de.mm20.launcher2.ui.icons.*
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
@ -54,13 +52,13 @@ class BatteryPartProvider : PartProvider {
} }
@Composable @Composable
override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) { override fun Component(compactLayout: Boolean) {
val batteryInfo by this.batteryInfo.collectAsState(null) val batteryInfo by this.batteryInfo.collectAsState(null)
batteryInfo?.let { batteryInfo?.let {
if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) { if (!compactLayout) {
Row( Row(
Modifier.padding(8.dp), Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
@ -86,7 +84,7 @@ class BatteryPartProvider : PartProvider {
} }
} }
} }
if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Horizontal) { if (compactLayout) {
Row( Row(
Modifier.padding(8.dp), Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically

View File

@ -12,7 +12,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.em import androidx.compose.ui.unit.em
import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.base.LocalTime import de.mm20.launcher2.ui.base.LocalTime
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
@ -25,9 +24,9 @@ class DatePartProvider : PartProvider {
} }
@Composable @Composable
override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) { override fun Component(compactLayout: Boolean) {
val time = LocalTime.current val time = LocalTime.current
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical val verticalLayout = !compactLayout
val context = LocalContext.current val context = LocalContext.current
TextButton( TextButton(
colors = ButtonDefaults.textButtonColors( colors = ButtonDefaults.textButtonColors(

View File

@ -3,16 +3,13 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts
import android.content.Context import android.content.Context
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.services.favorites.FavoritesService
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
import de.mm20.launcher2.widgets.CalendarWidget import de.mm20.launcher2.widgets.CalendarWidget
@ -27,25 +24,24 @@ class FavoritesPartProvider : PartProvider, KoinComponent {
private val favoritesService: FavoritesService by inject() private val favoritesService: FavoritesService by inject()
private val widgetRepository: WidgetRepository 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<Int> = flow { override fun getRanking(context: Context): Flow<Int> = flow {
emit(Int.MAX_VALUE) emit(Int.MAX_VALUE)
} }
@Composable @Composable
override fun Component(layout: ClockWidgetLayout) { override fun Component(compactLayout: Boolean) {
val columns by remember(layout) { val columns by remember {
dataStore.data.map { uiSettings.gridSettings.map {
val c = it.grid.columnCount it.columnCount
if (layout == ClockWidgetLayout.Horizontal) c - 2 else c
} }
}.collectAsState(0) }.collectAsState(0)
val excludeCalendar by remember { widgetRepository.exists(CalendarWidget.Type) }.collectAsState( val excludeCalendar by remember { widgetRepository.exists(CalendarWidget.Type) }.collectAsState(
true true
) )
val favorites by remember(columns, excludeCalendar, layout) { val favorites by remember(columns, excludeCalendar) {
favoritesService.getFavorites( favoritesService.getFavorites(
excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"), excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"),
manuallySorted = true, manuallySorted = true,

View File

@ -28,7 +28,6 @@ import androidx.compose.ui.unit.dp
import de.mm20.launcher2.music.MusicService import de.mm20.launcher2.music.MusicService
import de.mm20.launcher2.music.PlaybackState import de.mm20.launcher2.music.PlaybackState
import de.mm20.launcher2.music.SupportedActions import de.mm20.launcher2.music.SupportedActions
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
@ -48,7 +47,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
} }
@Composable @Composable
override fun Component(layout: ClockWidgetLayout) { override fun Component(compactLayout: Boolean) {
val context = LocalContext.current val context = LocalContext.current
val title by musicService.title.collectAsState(null) 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) val playIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause)
if (layout === ClockWidgetLayout.Horizontal) { if (compactLayout) {
Row( Row(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@ -113,7 +112,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
} }
} }
} }
if (layout === ClockWidgetLayout.Vertical) { if (!compactLayout) {
Column( Column(
modifier = Modifier.padding(8.dp), modifier = Modifier.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally

View File

@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts
import android.content.Context import android.content.Context
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface PartProvider { interface PartProvider {
@ -12,5 +11,5 @@ interface PartProvider {
fun setTime(time: Long) {} fun setTime(time: Long) {}
@Composable @Composable
fun Component(layout: ClockWidgetLayout) fun Component(compactLayout: Boolean)
} }

View File

@ -7,6 +7,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.Star
import androidx.compose.material.icons.rounded.Tag import androidx.compose.material.icons.rounded.Tag
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -30,6 +31,10 @@ fun FavoritesWidget(widget: FavoritesWidget) {
val tagsExpanded by viewModel.tagsExpanded.collectAsState(false) val tagsExpanded by viewModel.tagsExpanded.collectAsState(false)
LaunchedEffect(widget) {
viewModel.updateWidget(widget)
}
Column { Column {
if (favorites.isNotEmpty()) { if (favorites.isNotEmpty()) {
SearchResultGrid(favorites) SearchResultGrid(favorites)

View File

@ -1,32 +1,34 @@
package de.mm20.launcher2.ui.launcher.widgets.favorites 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 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 de.mm20.launcher2.ui.common.FavoritesVM
import kotlinx.coroutines.flow.Flow import de.mm20.launcher2.widgets.FavoritesWidget
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine 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 import org.koin.core.component.inject
class FavoritesWidgetVM : FavoritesVM() { class FavoritesWidgetVM : FavoritesVM() {
private val widgetsService: WidgetsService by inject() private val widgetsService: WidgetsService by inject()
override val tagsExpanded: Flow<Boolean> = dataStore.data.map { it.ui.widgetTagsMultiline } private val uiSettings: UiSettings by inject()
.shareIn(viewModelScope, SharingStarted.Lazily)
override val tagsExpanded: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val widget = MutableStateFlow<FavoritesWidget?>(null)
private val isTopWidget = widgetsService.isFavoritesWidgetFirst() private val isTopWidget = widgetsService.isFavoritesWidgetFirst()
private val clockWidgetFavSlots = dataStore.data.combine(isTopWidget) { data, isTop -> private val clockWidgetFavSlots =
if (!isTop || !data.clockWidget.favoritesPart) 0 combine(uiSettings.dock, isTopWidget, uiSettings.gridSettings) { (dock, isTop, grid) ->
else { dock as Boolean
if (data.clockWidget.layout == ClockWidgetLayout.Horizontal) data.grid.columnCount - 2 isTop as Boolean
else data.grid.columnCount grid as GridSettings
if (!isTop || !dock) 0
else {
grid.columnCount
}
} }
}
override val favorites = super.favorites.combine(clockWidgetFavSlots) { favs, slots -> override val favorites = super.favorites.combine(clockWidgetFavSlots) { favs, slots ->
if (selectedTag.value == null) { if (selectedTag.value == null) {
@ -38,17 +40,16 @@ class FavoritesWidgetVM : FavoritesVM() {
} }
override fun setTagsExpanded(expanded: Boolean) { override fun setTagsExpanded(expanded: Boolean) {
viewModelScope.launch { val widget = this.widget.value ?: return
dataStore.updateData { widgetsService.updateWidget(
it.toBuilder() widget.copy(
.setUi( config = widget.config.copy(tagsMultiline = expanded)
it.ui.toBuilder() )
.setWidgetTagsMultiline(expanded) )
.build() }
)
.build() fun updateWidget(widget: FavoritesWidget) {
} tagsExpanded.value = widget.config.tagsMultiline
}
} }
} }

View File

@ -5,11 +5,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.* import androidx.lifecycle.*
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager 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.DailyForecast
import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.weather.Forecast
import de.mm20.launcher2.weather.WeatherRepository import de.mm20.launcher2.weather.WeatherRepository
import de.mm20.launcher2.weather.settings.WeatherSettings
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -26,8 +25,6 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val dataStore: LauncherDataStore by inject()
/** /**
* Index of the currently selected day in [dailyForecasts] * Index of the currently selected day in [dailyForecasts]
*/ */
@ -111,7 +108,8 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
val autoLocation = weatherSettings.autoLocation val autoLocation = weatherSettings.autoLocation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .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) { fun selectDay(index: Int) {
selectedDayIndex = min(index, forecasts.lastIndex) selectedDayIndex = min(index, forecasts.lastIndex)

View File

@ -5,8 +5,8 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.navigation.NavController import androidx.navigation.NavController
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.ui.CardStyle
import de.mm20.launcher2.preferences.Settings.GridSettings import de.mm20.launcher2.preferences.ui.GridSettings
import de.mm20.launcher2.ui.theme.WallpaperColors import de.mm20.launcher2.ui.theme.WallpaperColors
val LocalWindowSize = compositionLocalOf { Size(0f, 0f) } val LocalWindowSize = compositionLocalOf { Size(0f, 0f) }
@ -15,11 +15,11 @@ val LocalAppWidgetHost = compositionLocalOf<AppWidgetHost?>(defaultFactory = { n
val LocalNavController = compositionLocalOf<NavController?> { null } val LocalNavController = compositionLocalOf<NavController?> { null }
val LocalCardStyle = compositionLocalOf<Settings.CardSettings> { Settings.CardSettings.getDefaultInstance() } val LocalCardStyle = compositionLocalOf { CardStyle() }
val LocalFavoritesEnabled = compositionLocalOf { true } val LocalFavoritesEnabled = compositionLocalOf { true }
val LocalGridSettings = compositionLocalOf { GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build() } val LocalGridSettings = compositionLocalOf { GridSettings() }
val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() } val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() }

View File

@ -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.backup.BackupSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen 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.ThemeSettingsScreen
import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
@ -170,9 +169,6 @@ class SettingsActivity : BaseActivity() {
composable(ROUTE_MEDIA_INTEGRATION) { composable(ROUTE_MEDIA_INTEGRATION) {
MediaIntegrationSettingsScreen() MediaIntegrationSettingsScreen()
} }
composable("settings/homescreen/clock") {
ClockWidgetSettingsScreen()
}
composable("settings/favorites") { composable("settings/favorites") {
FavoritesSettingsScreen() FavoritesSettingsScreen()
} }

View File

@ -9,9 +9,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.Settings.AppearanceSettings import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.ListPreference import de.mm20.launcher2.ui.component.preferences.ListPreference
import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.Preference
@ -26,22 +25,22 @@ fun AppearanceSettingsScreen() {
val viewModel: AppearanceSettingsScreenVM = viewModel() val viewModel: AppearanceSettingsScreenVM = viewModel()
val context = LocalContext.current val context = LocalContext.current
val navController = LocalNavController.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)) { PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item { item {
PreferenceCategory { PreferenceCategory {
val theme by viewModel.theme.collectAsState() val theme by viewModel.colorScheme.collectAsState()
ListPreference( ListPreference(
title = stringResource(id = R.string.preference_theme), title = stringResource(id = R.string.preference_theme),
items = listOf( items = listOf(
stringResource(id = R.string.preference_theme_system) to Theme.System, stringResource(id = R.string.preference_theme_system) to ColorScheme.System,
stringResource(id = R.string.preference_theme_light) to Theme.Light, stringResource(id = R.string.preference_theme_light) to ColorScheme.Light,
stringResource(id = R.string.preference_theme_dark) to Theme.Dark, stringResource(id = R.string.preference_theme_dark) to ColorScheme.Dark,
), ),
value = theme, value = theme,
onValueChanged = { newValue -> onValueChanged = { newValue ->
if (newValue == null) return@ListPreference if (newValue == null) return@ListPreference
viewModel.setTheme(newValue) viewModel.setColorScheme(newValue)
} }
) )
Preference( Preference(
@ -55,8 +54,8 @@ fun AppearanceSettingsScreen() {
ListPreference( ListPreference(
title = stringResource(R.string.preference_font), title = stringResource(R.string.preference_font),
items = listOf( items = listOf(
"Outfit" to AppearanceSettings.Font.Outfit, "Outfit" to Font.Outfit,
stringResource(R.string.preference_font_system) to AppearanceSettings.Font.SystemDefault, stringResource(R.string.preference_font_system) to Font.System,
), ),
value = font, value = font,
onValueChanged = { onValueChanged = {

View File

@ -3,44 +3,30 @@ package de.mm20.launcher2.ui.settings.appearance
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.ThemeRepository
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import java.util.UUID
class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { 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() private val themeRepository: ThemeRepository by inject()
val theme = dataStore.data.map { it.appearance.theme } val colorScheme = uiSettings.colorScheme
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setTheme(theme: Theme) { fun setColorScheme(colorScheme: ColorScheme) {
viewModelScope.launch { uiSettings.setColorScheme(colorScheme)
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setTheme(theme))
.build()
}
}
} }
val colorSchemeName = dataStore.data.map { val themeName = uiSettings.theme.flatMapLatest {
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let {
UUID.fromString(it)
}
}
.flatMapLatest {
themeRepository.getThemeOrDefault(it) themeRepository.getThemeOrDefault(it)
}.map { }.map {
it.name it.name
@ -48,16 +34,10 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val font = dataStore.data.map { it.appearance.font } val font = uiSettings.font
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFont(font: Font) { fun setFont(font: Font) {
viewModelScope.launch { uiSettings.setFont(font)
dataStore.updateData {
it.toBuilder()
.setAppearance(it.appearance.toBuilder().setFont(font))
.build()
}
}
} }
} }

View File

@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings.buildinfo
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import de.mm20.launcher2.accounts.AccountType import de.mm20.launcher2.accounts.AccountType
import de.mm20.launcher2.accounts.AccountsRepository import de.mm20.launcher2.accounts.AccountsRepository
import de.mm20.launcher2.preferences.Settings.WeatherSettings.WeatherProvider
import de.mm20.launcher2.weather.WeatherRepository import de.mm20.launcher2.weather.WeatherRepository
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent

View File

@ -15,7 +15,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel 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.R
import de.mm20.launcher2.ui.component.LauncherCard import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.component.preferences.ListPreference import de.mm20.launcher2.ui.component.preferences.ListPreference
@ -26,6 +28,9 @@ import de.mm20.launcher2.ui.component.preferences.SliderPreference
@Composable @Composable
fun CardsSettingsScreen() { fun CardsSettingsScreen() {
val viewModel: CardsSettingsScreenVM = viewModel() val viewModel: CardsSettingsScreenVM = viewModel()
val cardStyle by viewModel.cardStyle.collectAsState(CardStyle())
PreferenceScreen(title = stringResource(R.string.preference_cards)) { PreferenceScreen(title = stringResource(R.string.preference_cards)) {
item { item {
Box( Box(
@ -43,23 +48,21 @@ fun CardsSettingsScreen() {
} }
item { item {
PreferenceCategory { PreferenceCategory {
val shape by viewModel.shape.collectAsState()
ListPreference( ListPreference(
icon = Icons.Rounded.Rectangle, icon = Icons.Rounded.Rectangle,
title = stringResource(R.string.preference_cards_shape), title = stringResource(R.string.preference_cards_shape),
items = listOf( items = listOf(
stringResource(R.string.preference_cards_shape_rounded) to Settings.CardSettings.Shape.Rounded, stringResource(R.string.preference_cards_shape_rounded) to SurfaceShape.Rounded,
stringResource(R.string.preference_cards_shape_cut) to Settings.CardSettings.Shape.Cut, stringResource(R.string.preference_cards_shape_cut) to SurfaceShape.Cut,
), ),
value = shape, value = cardStyle.shape,
onValueChanged = { onValueChanged = {
if (it != null) viewModel.setShape(it) viewModel.setShape(it)
}) })
val radius by viewModel.radius.collectAsState()
SliderPreference( SliderPreference(
title = stringResource(R.string.preference_cards_corner_radius), title = stringResource(R.string.preference_cards_corner_radius),
icon = Icons.Rounded.RoundedCorner, icon = Icons.Rounded.RoundedCorner,
value = radius, value = cardStyle.cornerRadius,
min = 0, min = 0,
max = 24, max = 24,
step = 1, step = 1,
@ -67,22 +70,20 @@ fun CardsSettingsScreen() {
viewModel.setRadius(it) viewModel.setRadius(it)
} }
) )
val opacity by viewModel.opacity.collectAsState()
SliderPreference( SliderPreference(
title = stringResource(R.string.preference_cards_opacity), title = stringResource(R.string.preference_cards_opacity),
icon = Icons.Rounded.Opacity, icon = Icons.Rounded.Opacity,
value = opacity, value = cardStyle.opacity,
min = 0f, min = 0f,
max = 1f, max = 1f,
onValueChanged = { onValueChanged = {
viewModel.setOpacity(it) viewModel.setOpacity(it)
} }
) )
val borderWidth by viewModel.borderWidth.collectAsState()
SliderPreference( SliderPreference(
title = stringResource(R.string.preference_cards_stroke_width), title = stringResource(R.string.preference_cards_stroke_width),
icon = Icons.Rounded.LineWeight, icon = Icons.Rounded.LineWeight,
value = borderWidth, value = cardStyle.borderWidth,
min = 0, min = 0,
max = 8, max = 8,
step = 1, step = 1,

View File

@ -1,70 +1,29 @@
package de.mm20.launcher2.ui.settings.cards package de.mm20.launcher2.ui.settings.cards
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.ui.UiSettings
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 org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class CardsSettingsScreenVM: ViewModel(), KoinComponent { class CardsSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject() 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) { fun setOpacity(opacity: Float) {
viewModelScope.launch { uiSettings.setCardOpacity(opacity)
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setOpacity(opacity)
).build()
}
}
} }
val radius = dataStore.data.map { it.cards.radius }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setRadius(radius: Int) { fun setRadius(radius: Int) {
viewModelScope.launch { uiSettings.setCardRadius(radius)
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setRadius(radius)
).build()
}
}
} }
val borderWidth = dataStore.data.map { it.cards.borderWidth }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
fun setBorderWidth(borderWidth: Int) { fun setBorderWidth(borderWidth: Int) {
viewModelScope.launch { uiSettings.setCardBorderWidth(borderWidth)
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setBorderWidth(borderWidth)
).build()
}
}
} }
val shape = dataStore.data.map { it.cards.shape } fun setShape(shape: SurfaceShape) {
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) uiSettings.setCardShape(shape)
fun setShape(shape: Settings.CardSettings.Shape) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setShape(shape)
).build()
}
}
} }
} }

View File

@ -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)
}
}
)
}
}

View File

@ -2,9 +2,10 @@ package de.mm20.launcher2.ui.settings.clockwidget
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.ClockWidgetAlignment
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings import de.mm20.launcher2.preferences.ClockWidgetColors
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors import de.mm20.launcher2.preferences.ClockWidgetStyle
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -13,146 +14,62 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent { class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject() private val settings: ClockWidgetSettings by inject()
val layout = dataStore.data.map { it.clockWidget.layout } val compact = settings.compact
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLayout(layout: ClockWidgetSettings.ClockWidgetLayout) { fun setCompact(compact: Boolean) {
viewModelScope.launch { settings.setCompact(compact)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setLayout(layout)
).build()
}
}
} }
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle } val clockStyle = settings.clockStyle
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setClockStyle(clockStyle: ClockWidgetSettings.ClockStyle) { fun setClockStyle(clockStyle: ClockWidgetStyle) {
viewModelScope.launch { settings.setClockStyle(clockStyle)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setClockStyle(clockStyle)
).build()
}
}
} }
val color = dataStore.data.map { it.clockWidget.color } val color = settings.color
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setColor(color: ClockWidgetColors) { fun setColor(color: ClockWidgetColors) {
viewModelScope.launch { settings.setColor(color)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setColor(color)
).build()
}
}
} }
val fillHeight = dataStore.data.map { it.clockWidget.fillHeight } val fillHeight = settings.fillHeight
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFillHeight(fillHeight: Boolean) { fun setFillHeight(fillHeight: Boolean) {
viewModelScope.launch { settings.setFillHeight(fillHeight)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setFillHeight(fillHeight)
).build()
}
}
} }
val datePart = dataStore.data.map { it.clockWidget.datePart } val dock = settings.dock
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val parts = settings.parts
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setDatePart(datePart: Boolean) { fun setDatePart(datePart: Boolean) {
viewModelScope.launch { settings.setDatePart(datePart)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setDatePart(datePart)
).build()
}
}
} }
val favoritesPart = dataStore.data.map { it.clockWidget.favoritesPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFavoritesPart(favoritesPart: Boolean) { fun setFavoritesPart(favoritesPart: Boolean) {
viewModelScope.launch { settings.setDock(favoritesPart)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setFavoritesPart(favoritesPart)
).build()
}
}
} }
val batteryPart = dataStore.data.map { it.clockWidget.batteryPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBatteryPart(batteryPart: Boolean) { fun setBatteryPart(batteryPart: Boolean) {
viewModelScope.launch { settings.setBatteryPart(batteryPart)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setBatteryPart(batteryPart)
).build()
}
}
} }
val musicPart = dataStore.data.map { it.clockWidget.musicPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setMusicPart(musicPart: Boolean) { fun setMusicPart(musicPart: Boolean) {
viewModelScope.launch { settings.setMusicPart(musicPart)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setMusicPart(musicPart)
).build()
}
}
} }
val alarmPart = dataStore.data.map { it.clockWidget.alarmPart }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAlarmPart(alarmPart: Boolean) { fun setAlarmPart(alarmPart: Boolean) {
viewModelScope.launch { settings.setAlarmPart(alarmPart)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setAlarmPart(alarmPart)
).build()
}
}
} }
val alignment = dataStore.data.map { it.clockWidget.alignment } val alignment = settings.alignment
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAlignment(alignment: ClockWidgetSettings.ClockWidgetAlignment) { fun setAlignment(alignment: ClockWidgetAlignment) {
viewModelScope.launch { settings.setAlignment(alignment)
dataStore.updateData {
it.toBuilder()
.setClockWidget(
it.clockWidget.toBuilder()
.setAlignment(alignment)
).build()
}
}
} }
} }

View File

@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.colorscheme
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.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.DefaultThemeId
import de.mm20.launcher2.themes.Theme import de.mm20.launcher2.themes.Theme
import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.ThemeRepository
import de.mm20.launcher2.themes.fromJson
import de.mm20.launcher2.themes.toJson import de.mm20.launcher2.themes.toJson
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -31,12 +27,14 @@ import java.util.UUID
class ThemesSettingsScreenVM : ViewModel(), KoinComponent { class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
private val themeRepository: ThemeRepository by inject() private val themeRepository: ThemeRepository by inject()
private val dataStore: LauncherDataStore by inject() private val uiSettings: UiSettings by inject()
val selectedTheme: Flow<UUID?> = dataStore.data.map { val selectedTheme = uiSettings.theme.map {
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let { when(it) {
UUID.fromString(it) ThemeDescriptor.Default -> DefaultThemeId
} ?: DefaultThemeId ThemeDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
is ThemeDescriptor.Custom -> UUID.fromString(it.id)
}
} }
val themes: Flow<List<Theme>> = themeRepository.getThemes() val themes: Flow<List<Theme>> = themeRepository.getThemes()
@ -49,15 +47,10 @@ class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
} }
fun selectTheme(theme: Theme) { fun selectTheme(theme: Theme) {
viewModelScope.launch { when(theme.id) {
dataStore.updateData { DefaultThemeId -> ThemeDescriptor.Default
it.toBuilder() BlackAndWhiteThemeId -> ThemeDescriptor.BlackAndWhite
.setAppearance( else -> ThemeDescriptor.Custom(theme.id.toString())
it.appearance.toBuilder()
.setThemeId(theme.id.toString())
)
.build()
}
} }
} }

View File

@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.easteregg
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class EasterEggSettingsScreenVM: ViewModel(), KoinComponent { 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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setEasterEgg(easterEgg: Boolean) { fun setEasterEgg(easterEgg: Boolean) {
viewModelScope.launch { settings.setIconShape(if (easterEgg) IconShape.EasterEgg else IconShape.PlatformDefault)
dataStore.updateData {
it.toBuilder().setEasterEgg(easterEgg).build()
}
}
} }
} }

View File

@ -14,7 +14,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel 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.R
import de.mm20.launcher2.ui.component.preferences.ListPreference import de.mm20.launcher2.ui.component.preferences.ListPreference
import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.Preference

View File

@ -2,8 +2,9 @@ package de.mm20.launcher2.ui.settings.favorites
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.WeightFactor
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor import de.mm20.launcher2.preferences.search.FavoritesSettings
import de.mm20.launcher2.preferences.search.RankingSettings
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -12,65 +13,30 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class FavoritesSettingsScreenVM: ViewModel(), KoinComponent { 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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFrequentlyUsed(frequentlyUsed: Boolean) { fun setFrequentlyUsed(frequentlyUsed: Boolean) {
viewModelScope.launch { favoritesSettings.setFrequentlyUsed(frequentlyUsed)
dataStore.updateData {
it.toBuilder()
.setFavorites(
it.favorites.toBuilder()
.setFrequentlyUsed(frequentlyUsed)
)
.build()
}
}
} }
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows } val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) { fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
viewModelScope.launch { favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows)
dataStore.updateData {
it.toBuilder()
.setFavorites(
it.favorites.toBuilder()
.setFrequentlyUsedRows(frequentlyUsedRows)
)
.build()
}
}
} }
val editButton = dataStore.data.map { it.favorites.editButton } val editButton = favoritesSettings.showEditButton
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setEditButton(editButton: Boolean) { fun setEditButton(editButton: Boolean) {
viewModelScope.launch { favoritesSettings.setShowEditButton(editButton)
dataStore.updateData {
it.toBuilder()
.setFavorites(
it.favorites.toBuilder()
.setEditButton(editButton)
)
.build()
}
}
} }
val searchResultWeightFactor = dataStore.data.map { it.resultOrdering.weightFactor } val searchResultWeightFactor = rankingSettings.weightFactor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), WeightFactor.Default) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), WeightFactor.Default)
fun setSearchResultWeightFactor(searchResultWeightFactor: WeightFactor) { fun setSearchResultWeightFactor(searchResultWeightFactor: WeightFactor) {
viewModelScope.launch { rankingSettings.setWeightFactor(searchResultWeightFactor)
dataStore.updateData {
it.toBuilder()
.setResultOrdering(
it.resultOrdering.toBuilder()
.setWeightFactor(searchResultWeightFactor)
)
.build()
}
}
} }
} }

View File

@ -7,11 +7,11 @@ import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.accounts.Account import de.mm20.launcher2.accounts.Account
import de.mm20.launcher2.accounts.AccountType import de.mm20.launcher2.accounts.AccountType
import de.mm20.launcher2.accounts.AccountsRepository import de.mm20.launcher2.accounts.AccountsRepository
import de.mm20.launcher2.files.settings.FileSearchSettings
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.search.FileSearchSettings
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View File

@ -27,8 +27,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings.Layout
import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.common.SearchablePicker import de.mm20.launcher2.ui.common.SearchablePicker
@ -47,14 +48,14 @@ fun GestureSettingsScreen() {
val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null) val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null)
val options = buildList { val options = buildList {
add(stringResource(R.string.gesture_action_none) to GestureAction.None) add(stringResource(R.string.gesture_action_none) to GestureAction.NoAction)
add(stringResource(R.string.gesture_action_notifications) to GestureAction.OpenNotificationDrawer) add(stringResource(R.string.gesture_action_notifications) to GestureAction.Notifications)
add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.OpenQuickSettings) add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.QuickSettings)
if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.LockScreen) if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.ScreenLock)
add(stringResource(R.string.gesture_action_recents) to GestureAction.OpenRecents) add(stringResource(R.string.gesture_action_recents) to GestureAction.Recents)
add(stringResource(R.string.gesture_action_power_menu) to GestureAction.OpenPowerDialog) add(stringResource(R.string.gesture_action_power_menu) to GestureAction.PowerMenu)
add(stringResource(R.string.gesture_action_open_search) to GestureAction.OpenSearch) add(stringResource(R.string.gesture_action_open_search) to GestureAction.Search)
add(stringResource(R.string.gesture_action_launch_app) to GestureAction.LaunchApp) add(stringResource(R.string.gesture_action_launch_app) to GestureAction.Launch(null))
} }
val context = LocalContext.current val context = LocalContext.current
@ -64,9 +65,9 @@ fun GestureSettingsScreen() {
val baseLayout by viewModel.baseLayout.collectAsStateWithLifecycle(null) val baseLayout by viewModel.baseLayout.collectAsStateWithLifecycle(null)
ListPreference(title = stringResource(R.string.preference_layout_open_search), ListPreference(title = stringResource(R.string.preference_layout_open_search),
items = listOf( items = listOf(
stringResource(R.string.open_search_pull_down) to Layout.PullDown, stringResource(R.string.open_search_pull_down) to BaseLayout.PullDown,
stringResource(R.string.open_search_swipe_left) to Layout.Pager, stringResource(R.string.open_search_swipe_left) to BaseLayout.Pager,
stringResource(R.string.open_search_swipe_right) to Layout.PagerReversed, stringResource(R.string.open_search_swipe_right) to BaseLayout.PagerReversed,
), ),
value = baseLayout, value = baseLayout,
onValueChanged = { onValueChanged = {
@ -125,7 +126,7 @@ fun GestureSettingsScreen() {
) )
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null) val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
val swipeDownIsSearch = layout == Layout.PullDown val swipeDownIsSearch = layout == BaseLayout.PullDown
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown) && !swipeDownIsSearch) { AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown) && !swipeDownIsSearch) {
MissingPermissionBanner( MissingPermissionBanner(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
@ -149,7 +150,7 @@ fun GestureSettingsScreen() {
) )
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null) val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
val swipeLeftIsSearch = layout == Layout.Pager val swipeLeftIsSearch = layout == BaseLayout.Pager
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft) && !swipeLeftIsSearch) { AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft) && !swipeLeftIsSearch) {
MissingPermissionBanner( MissingPermissionBanner(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
@ -173,7 +174,7 @@ fun GestureSettingsScreen() {
) )
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null) val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
val swipeRightIsSearch = layout == Layout.PagerReversed val swipeRightIsSearch = layout == BaseLayout.PagerReversed
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight) && !swipeRightIsSearch) { AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight) && !swipeRightIsSearch) {
MissingPermissionBanner( MissingPermissionBanner(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp),
@ -225,11 +226,11 @@ fun GestureSettingsScreen() {
fun requiresAccessibilityService(action: GestureAction?): Boolean { fun requiresAccessibilityService(action: GestureAction?): Boolean {
return when (action) { return when (action) {
GestureAction.OpenNotificationDrawer, is GestureAction.Notifications,
GestureAction.LockScreen, is GestureAction.ScreenLock,
GestureAction.OpenQuickSettings, is GestureAction.QuickSettings,
GestureAction.OpenRecents, is GestureAction.Recents,
GestureAction.OpenPowerDialog -> true is GestureAction.PowerMenu -> true
else -> false else -> false
} }
} }
@ -256,12 +257,12 @@ fun GesturePreference(
title = title, title = title,
enabled = !isOpenSearch, enabled = !isOpenSearch,
items = options, items = options,
value = if (isOpenSearch) GestureAction.OpenSearch else value, value = if (isOpenSearch) GestureAction.Search else value,
onValueChanged = { if (it != null) onValueChanged(it) } onValueChanged = { if (it != null) onValueChanged(it) }
) )
} }
if (value == GestureAction.LaunchApp && !isOpenSearch) { if (value is GestureAction.Launch && !isOpenSearch) {
Box( Box(
modifier = Modifier modifier = Modifier
.height(36.dp) .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( SearchablePicker(
title = { Text(title) }, title = { Text(title) },
onDismissRequest = { onDismissRequest = {
showAppPicker = false showAppPicker = false
if (app == null) onValueChanged(GestureAction.None) if (app == null) onValueChanged(GestureAction.NoAction)
}, },
value = app, value = app,
onValueChanged = { onValueChanged = {

View File

@ -3,15 +3,16 @@ package de.mm20.launcher2.ui.settings.gestures
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.BaseLayout
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.GestureAction
import de.mm20.launcher2.preferences.Settings.GestureSettings.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.search.SavableSearchable
import de.mm20.launcher2.searchable.SavableSearchableRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
@ -22,7 +23,8 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class GestureSettingsScreenVM : ViewModel(), KoinComponent { 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 permissionsManager: PermissionsManager by inject()
private val searchableRepository: SavableSearchableRepository by inject() private val searchableRepository: SavableSearchableRepository by inject()
private val iconService: IconService by inject() private val iconService: IconService by inject()
@ -30,208 +32,124 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility) val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val baseLayout = dataStore.data.map { it.layout.baseLayout } val baseLayout = uiSettings.baseLayout
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBaseLayout(baseLayout: Settings.LayoutSettings.Layout) { fun setBaseLayout(baseLayout: BaseLayout) {
viewModelScope.launch { uiSettings.setBaseLayout(baseLayout)
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setBaseLayout(baseLayout))
.build()
}
}
} }
val swipeDown = dataStore.data.map { it.gestures.swipeDown }
val swipeDown = gestureSettings.swipeDown
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val swipeLeft = dataStore.data.map { it.gestures.swipeLeft } val swipeLeft = gestureSettings.swipeLeft
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val swipeRight = dataStore.data.map { it.gestures.swipeRight } val swipeRight = gestureSettings.swipeRight
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val doubleTap = dataStore.data.map { it.gestures.doubleTap } val doubleTap = gestureSettings.doubleTap
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val longPress = dataStore.data.map { it.gestures.longPress } val longPress = gestureSettings.longPress
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val homeButton = dataStore.data.map { it.gestures.homeButton } val homeButton = gestureSettings.homeButton
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSwipeDown(action: GestureAction) { fun setSwipeDown(action: GestureAction) {
viewModelScope.launch { gestureSettings.setSwipeDown(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeDown(action).build())
.build()
}
}
} }
fun setSwipeLeft(action: GestureAction) { fun setSwipeLeft(action: GestureAction) {
viewModelScope.launch { gestureSettings.setSwipeLeft(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeLeft(action).build())
.build()
}
}
} }
fun setSwipeRight(action: GestureAction) { fun setSwipeRight(action: GestureAction) {
viewModelScope.launch { gestureSettings.setSwipeRight(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeRight(action).build())
.build()
}
}
} }
fun setDoubleTap(action: GestureAction) { fun setDoubleTap(action: GestureAction) {
viewModelScope.launch { gestureSettings.setDoubleTap(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setDoubleTap(action).build())
.build()
}
}
} }
fun setLongPress(action: GestureAction) { fun setLongPress(action: GestureAction) {
viewModelScope.launch { gestureSettings.setLongPress(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setLongPress(action).build())
.build()
}
}
} }
fun setHomeButton(action: GestureAction) { fun setHomeButton(action: GestureAction) {
viewModelScope.launch { gestureSettings.setHomeButton(action)
dataStore.updateData {
it.toBuilder().setGestures(it.gestures.toBuilder().setHomeButton(action).build())
.build()
}
}
} }
val swipeLeftApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeLeftApp } val swipeLeftApp: Flow<SavableSearchable?> = swipeLeft
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setSwipeLeftApp(searchable: SavableSearchable?) { fun setSwipeLeftApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setSwipeLeft(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setSwipeLeftApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
val swipeRightApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeRightApp } val swipeRightApp: Flow<SavableSearchable?> = swipeRight
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setSwipeRightApp(searchable: SavableSearchable?) { fun setSwipeRightApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setSwipeRight(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setSwipeRightApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
val swipeDownApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeDownApp } val swipeDownApp: Flow<SavableSearchable?> = swipeDown
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setSwipeDownApp(searchable: SavableSearchable?) { fun setSwipeDownApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setSwipeDown(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setSwipeDownApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
val longPressApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.longPressApp } val longPressApp: Flow<SavableSearchable?> = longPress
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setLongPressApp(searchable: SavableSearchable?) { fun setLongPressApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setLongPress(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setLongPressApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
val doubleTapApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.doubleTapApp } val doubleTapApp: Flow<SavableSearchable?> = doubleTap
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setDoubleTapApp(searchable: SavableSearchable?) { fun setDoubleTapApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setDoubleTap(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setDoubleTapApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
val homeButtonApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.homeButtonApp } val homeButtonApp: Flow<SavableSearchable?> = homeButton
.map { .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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
fun setHomeButtonApp(searchable: SavableSearchable?) { fun setHomeButtonApp(searchable: SavableSearchable?) {
viewModelScope.launch { searchable?.let { searchableRepository.insert(it) } ?: return
searchable?.let { searchableRepository.insert(it) } setHomeButton(GestureAction.Launch(searchable.key))
dataStore.updateData {
it.toBuilder()
.setGestures(it.gestures.toBuilder()
.setHomeButtonApp(searchable?.key ?: "")
.build()
)
.build()
}
}
} }
fun requestPermission(context: AppCompatActivity) { fun requestPermission(context: AppCompatActivity) {
permissionsManager.requestPermission(context, PermissionGroup.Accessibility) permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
} }

View File

@ -9,7 +9,7 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.ktx.isAtLeastApiLevel 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.SavableSearchable
import de.mm20.launcher2.search.Application import de.mm20.launcher2.search.Application
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -29,7 +29,7 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
private val appRepository: AppRepository by inject() private val appRepository: AppRepository by inject()
private val searchableRepository: SavableSearchableRepository by inject() private val searchableRepository: SavableSearchableRepository by inject()
private val iconService: IconService 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 { val allApps = appRepository.findMany().map {
withContext(Dispatchers.Default) { it.sorted() } withContext(Dispatchers.Default) { it.sorted() }
@ -68,16 +68,10 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
app.openAppDetails(context) app.openAppDetails(context)
} }
val hiddenItemsButton = dataStore.data.map { it.searchBar.hiddenItemsButton } val hiddenItemsButton = searchUiSettings.hiddenItemsButton
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setHiddenItemsButton(hidden: Boolean) { fun setHiddenItemsButton(hidden: Boolean) {
viewModelScope.launch { searchUiSettings.setHiddenItemsButton(hidden)
dataStore.updateData {
it.toBuilder()
.setSearchBar(it.searchBar.toBuilder().setHiddenItemsButton(hidden)).build()
}
}
} }
} }

View File

@ -26,7 +26,10 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.pager.HorizontalPagerIndicator 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.R
import de.mm20.launcher2.ui.component.SearchBar import de.mm20.launcher2.ui.component.SearchBar
import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.component.SearchBarLevel
@ -109,14 +112,14 @@ fun HomescreenSettingsScreen() {
viewModel.setSearchBarStyle(it) viewModel.setSearchBarStyle(it)
} }
) )
AnimatedVisibility(searchBarStyle == Settings.SearchBarSettings.SearchBarStyle.Transparent) { AnimatedVisibility(searchBarStyle == SearchBarStyle.Transparent) {
ListPreference( ListPreference(
title = stringResource(R.string.preference_search_bar_color), title = stringResource(R.string.preference_search_bar_color),
value = searchBarColor, value = searchBarColor,
items = listOf( items = listOf(
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SearchBarSettings.SearchBarColors.Auto, stringResource(R.string.preference_system_bar_icons_auto) to SearchBarColors.Auto,
stringResource(R.string.preference_system_bar_icons_light) to Settings.SearchBarSettings.SearchBarColors.Light, stringResource(R.string.preference_system_bar_icons_light) to SearchBarColors.Light,
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SearchBarSettings.SearchBarColors.Dark, stringResource(R.string.preference_system_bar_icons_dark) to SearchBarColors.Dark,
), ),
onValueChanged = { onValueChanged = {
if (it != null) viewModel.setSearchBarColor(it) if (it != null) viewModel.setSearchBarColor(it)
@ -208,9 +211,9 @@ fun HomescreenSettingsScreen() {
title = stringResource(R.string.preference_status_bar_icons), title = stringResource(R.string.preference_status_bar_icons),
value = lightStatusBar, value = lightStatusBar,
items = listOf( items = listOf(
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto, stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto,
stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light, stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light,
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark, stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark,
), ),
onValueChanged = { onValueChanged = {
if (it != null) viewModel.setLightStatusBar(it) if (it != null) viewModel.setLightStatusBar(it)
@ -220,9 +223,9 @@ fun HomescreenSettingsScreen() {
title = stringResource(R.string.preference_nav_bar_icons), title = stringResource(R.string.preference_nav_bar_icons),
value = lightNavBar, value = lightNavBar,
items = listOf( items = listOf(
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto, stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto,
stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light, stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light,
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark, stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark,
), ),
onValueChanged = { onValueChanged = {
if (it != null) viewModel.setLightNavBar(it) if (it != null) viewModel.setLightNavBar(it)
@ -255,15 +258,14 @@ fun HomescreenSettingsScreen() {
fun SearchBarStylePreference( fun SearchBarStylePreference(
title: String, title: String,
summary: String? = null, summary: String? = null,
value: Settings.SearchBarSettings.SearchBarStyle?, value: SearchBarStyle?,
onValueChanged: (Settings.SearchBarSettings.SearchBarStyle) -> Unit onValueChanged: (SearchBarStyle) -> Unit
) { ) {
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
Preference(title = title, summary = summary, onClick = { showDialog = true }) Preference(title = title, summary = summary, onClick = { showDialog = true })
if (showDialog && value != null) { if (showDialog && value != null) {
val styles = remember { val styles = remember {
Settings.SearchBarSettings.SearchBarStyle.values() SearchBarStyle.entries
.filter { it != Settings.SearchBarSettings.SearchBarStyle.UNRECOGNIZED }
} }
val pagerState = rememberPagerState(initialPage = styles.indexOf(value)) { styles.size } val pagerState = rememberPagerState(initialPage = styles.indexOf(value)) { styles.size }

View File

@ -13,8 +13,12 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.Settings 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.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -23,52 +27,31 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.get import org.koin.core.component.get
class HomescreenSettingsScreenVM( class HomescreenSettingsScreenVM(
private val dataStore: LauncherDataStore, private val uiSettings: UiSettings,
) : ViewModel() { ) : ViewModel() {
var showClockWidgetSheet by mutableStateOf(false) var showClockWidgetSheet by mutableStateOf(false)
val dimWallpaper = dataStore.data.map { it.appearance.dimWallpaper } val dimWallpaper = uiSettings.dimWallpaper
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setDimWallpaper(dimWallpaper: Boolean) { fun setDimWallpaper(dimWallpaper: Boolean) {
viewModelScope.launch { uiSettings.setDimWallpaper(dimWallpaper)
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setDimWallpaper(dimWallpaper)
).build()
}
}
} }
val blurWallpaper = dataStore.data.map { it.appearance.blurWallpaper } val blurWallpaper = uiSettings.blurWallpaper
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
fun setBlurWallpaper(blurWallpaper: Boolean) { fun setBlurWallpaper(blurWallpaper: Boolean) {
viewModelScope.launch { uiSettings.setBlurWallpaper(blurWallpaper)
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setBlurWallpaper(blurWallpaper)
).build()
}
}
} }
val blurWallpaperRadius = dataStore.data.map { it.appearance.blurWallpaperRadius } val blurWallpaperRadius = uiSettings.wallpaperBlurRadius
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32)
fun setBlurWallpaperRadius(blurWallpaperRadius: Int) { fun setBlurWallpaperRadius(blurWallpaperRadius: Int) {
viewModelScope.launch { uiSettings.setWallpaperBlurRadius(blurWallpaperRadius)
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setBlurWallpaperRadius(blurWallpaperRadius)
).build()
}
}
} }
fun openWallpaperChooser(context: AppCompatActivity) { fun openWallpaperChooser(context: AppCompatActivity) {
@ -80,168 +63,88 @@ class HomescreenSettingsScreenVM(
return context.getSystemService<WindowManager>()?.isCrossWindowBlurEnabled == true return context.getSystemService<WindowManager>()?.isCrossWindowBlurEnabled == true
} }
val statusBarIcons = dataStore.data.map { it.systemBars.statusBarColor } val statusBarIcons = uiSettings.statusBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightStatusBar(statusBarColor: Settings.SystemBarsSettings.SystemBarColors) {
viewModelScope.launch { fun setLightStatusBar(statusBarColor: SystemBarColors) {
dataStore.updateData { uiSettings.setStatusBarColor(statusBarColor)
it.toBuilder()
.setSystemBars(
it.systemBars.toBuilder()
.setStatusBarColor(statusBarColor)
)
.build()
}
}
} }
val navBarIcons = dataStore.data.map { it.systemBars.navBarColor } val navBarIcons = uiSettings.navigationBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLightNavBar(navBarColors: Settings.SystemBarsSettings.SystemBarColors) {
viewModelScope.launch { fun setLightNavBar(navBarColors: SystemBarColors) {
dataStore.updateData { uiSettings.setNavigationBarColor(navBarColors)
it.toBuilder()
.setSystemBars(
it.systemBars.toBuilder()
.setNavBarColor(navBarColors)
)
.build()
}
}
} }
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar } val hideStatusBar = uiSettings.hideStatusBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setHideStatusBar(hideStatusBar: Boolean) { fun setHideStatusBar(hideStatusBar: Boolean) {
viewModelScope.launch { uiSettings.setHideStatusBar(hideStatusBar)
dataStore.updateData {
it.toBuilder()
.setSystemBars(
it.systemBars.toBuilder()
.setHideStatusBar(hideStatusBar)
)
.build()
}
}
} }
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar } val hideNavBar = uiSettings.hideNavigationBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setHideNavBar(hideNavBar: Boolean) { fun setHideNavBar(hideNavBar: Boolean) {
viewModelScope.launch { uiSettings.setHideNavigationBar(hideNavBar)
dataStore.updateData {
it.toBuilder()
.setSystemBars(
it.systemBars.toBuilder()
.setHideNavBar(hideNavBar)
)
.build()
}
}
} }
val searchBarColor = dataStore.data.map { it.searchBar.color } val searchBarColor = uiSettings.searchBarColor
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchBarColor(color: Settings.SearchBarSettings.SearchBarColors) {
viewModelScope.launch { fun setSearchBarColor(color: SearchBarColors) {
dataStore.updateData { uiSettings.setSearchBarColor(color)
it.toBuilder()
.setSearchBar(
it.searchBar.toBuilder()
.setColor(color)
)
.build()
}
}
} }
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle } val searchBarStyle = uiSettings.searchBarStyle
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchBarStyle(searchBarStyle: Settings.SearchBarSettings.SearchBarStyle) {
viewModelScope.launch { fun setSearchBarStyle(searchBarStyle: SearchBarStyle) {
dataStore.updateData { uiSettings.setSearchBarStyle(searchBarStyle)
it.toBuilder()
.setSearchBar(
it.searchBar.toBuilder()
.setSearchBarStyle(searchBarStyle)
)
.build()
}
}
} }
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar } val fixedSearchBar = uiSettings.fixedSearchBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFixedSearchBar(fixedSearchBar: Boolean) { fun setFixedSearchBar(fixedSearchBar: Boolean) {
viewModelScope.launch { uiSettings.setFixedSearchBar(fixedSearchBar)
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setFixedSearchBar(fixedSearchBar))
.build()
}
}
} }
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar } val bottomSearchBar = uiSettings.bottomSearchBar
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setBottomSearchBar(bottomSearchBar: Boolean) { fun setBottomSearchBar(bottomSearchBar: Boolean) {
viewModelScope.launch { uiSettings.setBottomSearchBar(bottomSearchBar)
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setBottomSearchBar(bottomSearchBar))
.build()
}
}
} }
val fixedRotation = dataStore.data.map { it.layout.fixedRotation } val fixedRotation = uiSettings.orientation.map { it != ScreenOrientation.Auto }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFixedRotation(fixedRotation: Boolean) { fun setFixedRotation(fixedRotation: Boolean) {
viewModelScope.launch { uiSettings.setOrientation(if (fixedRotation) ScreenOrientation.Portrait else ScreenOrientation.Auto)
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setFixedRotation(fixedRotation))
.build()
}
}
} }
val widgetEditButton = dataStore.data.map { it.widgets.editButton } val widgetEditButton = uiSettings.widgetEditButton
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWidgetEditButton(editButton: Boolean) { fun setWidgetEditButton(editButton: Boolean) {
viewModelScope.launch { uiSettings.setWidgetEditButton(editButton)
dataStore.updateData {
it.toBuilder()
.setWidgets(
it.widgets.toBuilder()
.setEditButton(editButton)
)
.build()
}
}
} }
val chargingAnimation = dataStore.data.map { it.animations.charging } val chargingAnimation = uiSettings.chargingAnimation
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setChargingAnimation(chargingAnimation: Boolean) { fun setChargingAnimation(chargingAnimation: Boolean) {
viewModelScope.launch { uiSettings.setChargingAnimation(chargingAnimation)
dataStore.updateData {
it.toBuilder()
.setAnimations(
it.animations.toBuilder()
.setCharging(chargingAnimation)
)
.build()
}
}
} }
companion object : KoinComponent { companion object : KoinComponent {
val Factory = viewModelFactory { val Factory = viewModelFactory {
initializer { initializer {
HomescreenSettingsScreenVM( HomescreenSettingsScreenVM(
dataStore = get() uiSettings = get()
) )
} }
} }

View File

@ -24,7 +24,6 @@ import androidx.compose.material3.FilledIconToggleButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.PlainTooltipBox import androidx.compose.material3.PlainTooltipBox
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -50,7 +49,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.icons.StaticIconLayer import de.mm20.launcher2.icons.StaticIconLayer
import de.mm20.launcher2.icons.StaticLauncherIcon 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.R
import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.ShapedLauncherIcon 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.SwitchPreference
import de.mm20.launcher2.ui.component.preferences.label import de.mm20.launcher2.ui.component.preferences.label
import de.mm20.launcher2.ui.component.preferences.value import de.mm20.launcher2.ui.component.preferences.value
import de.mm20.launcher2.ui.ktx.toPixels
@Composable @Composable
fun IconsSettingsScreen() { fun IconsSettingsScreen() {
val viewModel: IconsSettingsScreenVM = viewModel(factory = IconsSettingsScreenVM.Factory) val viewModel: IconsSettingsScreenVM = viewModel(factory = IconsSettingsScreenVM.Factory)
val context = LocalContext.current 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 density = LocalDensity.current
val showLabels by viewModel.showLabels.collectAsStateWithLifecycle(null) val iconShape by viewModel.iconShape.collectAsStateWithLifecycle(IconShape.PlatformDefault)
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 iconPackPackage by viewModel.iconPack.collectAsStateWithLifecycle(null)
val iconPackThemed by viewModel.iconPackThemed.collectAsState(true)
val installedIconPacks by viewModel.installedIconPacks.collectAsState(emptyList()) val installedIconPacks by viewModel.installedIconPacks.collectAsState(emptyList())
val forceThemedIcons by viewModel.forceThemedIcons.collectAsStateWithLifecycle(null)
val hasNotificationsPermission by viewModel.hasNotificationsPermission.collectAsStateWithLifecycle(null) val hasNotificationsPermission by viewModel.hasNotificationsPermission.collectAsStateWithLifecycle(null)
@ -91,8 +86,8 @@ fun IconsSettingsScreen() {
val shortcutBadges by viewModel.shortcutBadges.collectAsStateWithLifecycle(null) val shortcutBadges by viewModel.shortcutBadges.collectAsStateWithLifecycle(null)
val pluginBadges by viewModel.pluginBadges.collectAsStateWithLifecycle(null) val pluginBadges by viewModel.pluginBadges.collectAsStateWithLifecycle(null)
val previewIcons by remember(iconSize) { val previewIcons by remember(grid?.iconSize) {
viewModel.getPreviewIcons(with(density) { iconSize.dp.toPx() }.toInt()) viewModel.getPreviewIcons(with(density) { grid.iconSize.dp.toPx() }.toInt())
}.collectAsState( }.collectAsState(
emptyList() emptyList()
) )
@ -102,7 +97,7 @@ fun IconsSettingsScreen() {
PreferenceCategory(title = stringResource(R.string.preference_category_grid)) { PreferenceCategory(title = stringResource(R.string.preference_category_grid)) {
SliderPreference( SliderPreference(
title = stringResource(R.string.preference_grid_icon_size), title = stringResource(R.string.preference_grid_icon_size),
value = iconSize, value = grid.iconSize,
step = 8, step = 8,
min = 32, min = 32,
max = 64, max = 64,
@ -113,14 +108,14 @@ fun IconsSettingsScreen() {
SwitchPreference( SwitchPreference(
title = stringResource(R.string.preference_grid_labels), title = stringResource(R.string.preference_grid_labels),
summary = stringResource(R.string.preference_grid_labels_summary), summary = stringResource(R.string.preference_grid_labels_summary),
value = showLabels == true, value = grid.showLabels,
onValueChanged = { onValueChanged = {
viewModel.setShowLabels(it) viewModel.setShowLabels(it)
} }
) )
SliderPreference( SliderPreference(
title = stringResource(R.string.preference_grid_column_count), title = stringResource(R.string.preference_grid_column_count),
value = columnCount, value = grid.columnCount,
min = 3, min = 3,
max = 12, max = 12,
onValueChanged = { onValueChanged = {
@ -148,7 +143,7 @@ fun IconsSettingsScreen() {
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
ShapedLauncherIcon(size = iconSize.dp, icon = { icon }) ShapedLauncherIcon(size = grid.iconSize.dp, icon = { icon })
} }
} }
} }
@ -165,7 +160,7 @@ fun IconsSettingsScreen() {
SwitchPreference( SwitchPreference(
title = stringResource(R.string.preference_enforce_icon_shape), title = stringResource(R.string.preference_enforce_icon_shape),
summary = stringResource(R.string.preference_enforce_icon_shape_summary), summary = stringResource(R.string.preference_enforce_icon_shape_summary),
value = adaptifyLegacyIcons == true, value = icons?.adaptify == true,
onValueChanged = { onValueChanged = {
viewModel.setAdaptifyLegacyIcons(it) viewModel.setAdaptifyLegacyIcons(it)
} }
@ -173,7 +168,7 @@ fun IconsSettingsScreen() {
SwitchPreference( SwitchPreference(
title = stringResource(R.string.preference_themed_icons), title = stringResource(R.string.preference_themed_icons),
summary = stringResource(R.string.preference_themed_icons_summary), summary = stringResource(R.string.preference_themed_icons_summary),
value = themedIcons == true, value = icons?.themedIcons == true,
onValueChanged = { onValueChanged = {
viewModel.setThemedIcons(it) viewModel.setThemedIcons(it)
} }
@ -181,14 +176,14 @@ fun IconsSettingsScreen() {
SwitchPreference( SwitchPreference(
title = stringResource(R.string.preference_force_themed_icons), title = stringResource(R.string.preference_force_themed_icons),
summary = stringResource(R.string.preference_force_themed_icons_summary), summary = stringResource(R.string.preference_force_themed_icons_summary),
value = forceThemedIcons == true, value = icons?.forceThemed == true,
enabled = themedIcons == true, enabled = icons?.themedIcons == true,
onValueChanged = { onValueChanged = {
viewModel.setForceThemedIcons(it) viewModel.setForceThemedIcons(it)
} }
) )
val iconPack by remember { val iconPack by remember {
derivedStateOf { installedIconPacks.firstOrNull { it.packageName == iconPackPackage } } derivedStateOf { installedIconPacks.firstOrNull { it.packageName == icons?.iconPack } }
} }
val items = installedIconPacks.map { val items = installedIconPacks.map {
it.name to it it.name to it
@ -265,7 +260,7 @@ fun IconsSettingsScreen() {
PlainTooltipBox(tooltip = { Text(stringResource(R.string.icon_pack_dynamic_colors)) }) { PlainTooltipBox(tooltip = { Text(stringResource(R.string.icon_pack_dynamic_colors)) }) {
FilledIconToggleButton( FilledIconToggleButton(
modifier = Modifier.tooltipTrigger(), modifier = Modifier.tooltipTrigger(),
checked = iconPackThemed, checked = icons?.iconPackThemed == true,
onCheckedChange = { onCheckedChange = {
viewModel.setIconPackThemed(it) viewModel.setIconPackThemed(it)
}) { }) {
@ -347,16 +342,16 @@ fun IconsSettingsScreen() {
fun IconShapePreference( fun IconShapePreference(
title: String, title: String,
summary: String? = null, summary: String? = null,
value: Settings.IconSettings.IconShape?, value: IconShape?,
onValueChanged: (Settings.IconSettings.IconShape) -> Unit onValueChanged: (IconShape) -> Unit
) { ) {
var showDialog by remember { mutableStateOf(false) } var showDialog by remember { mutableStateOf(false) }
Preference(title = title, summary = summary, onClick = { showDialog = true }) Preference(title = title, summary = summary, onClick = { showDialog = true })
if (showDialog && value != null) { if (showDialog && value != null) {
val shapes = remember { val shapes = remember {
Settings.IconSettings.IconShape.values() IconShape.entries
.filter { it != Settings.IconSettings.IconShape.UNRECOGNIZED && it != Settings.IconSettings.IconShape.EasterEgg } .filter { it != IconShape.EasterEgg }
} }
Dialog(onDismissRequest = { showDialog = false }) { Dialog(onDismissRequest = { showDialog = false }) {
Surface( Surface(
@ -431,19 +426,19 @@ fun IconShapePreference(
@Composable @Composable
private fun getShapeName(shape: Settings.IconSettings.IconShape?): String? { private fun getShapeName(shape: IconShape?): String? {
return stringResource( return stringResource(
when (shape) { when (shape) {
Settings.IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle IconShape.Triangle -> R.string.preference_icon_shape_triangle
Settings.IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon IconShape.Hexagon -> R.string.preference_icon_shape_hexagon
Settings.IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square
Settings.IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle IconShape.Squircle -> R.string.preference_icon_shape_squircle
Settings.IconSettings.IconShape.Square -> R.string.preference_icon_shape_square IconShape.Square -> R.string.preference_icon_shape_square
Settings.IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon IconShape.Pentagon -> R.string.preference_icon_shape_pentagon
Settings.IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform IconShape.PlatformDefault -> R.string.preference_icon_shape_platform
Settings.IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle IconShape.Circle -> R.string.preference_icon_shape_circle
Settings.IconSettings.IconShape.Teardrop -> R.string.preference_icon_shape_teardrop IconShape.Teardrop -> R.string.preference_icon_shape_teardrop
Settings.IconSettings.IconShape.Pebble -> R.string.preference_icon_shape_pebble IconShape.Pebble -> R.string.preference_icon_shape_pebble
else -> return null else -> return null
} }
) )

View File

@ -2,124 +2,64 @@ package de.mm20.launcher2.ui.settings.icons
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory import androidx.lifecycle.viewmodel.viewModelFactory
import de.mm20.launcher2.badges.settings.BadgeSettings
import de.mm20.launcher2.icons.IconPack import de.mm20.launcher2.icons.IconPack
import de.mm20.launcher2.icons.IconService import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.icons.LauncherIcon import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.IconShape
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.ui.BadgeSettings
import de.mm20.launcher2.search.Application import de.mm20.launcher2.preferences.ui.IconSettings
import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.services.favorites.FavoritesService import de.mm20.launcher2.services.favorites.FavoritesService
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.get import org.koin.core.component.get
class IconsSettingsScreenVM( class IconsSettingsScreenVM(
private val dataStore: LauncherDataStore, private val uiSettings: UiSettings,
private val iconSettings: IconSettings,
private val badgeSettings: BadgeSettings, private val badgeSettings: BadgeSettings,
private val iconService: IconService, private val iconService: IconService,
private val favoritesService: FavoritesService, private val favoritesService: FavoritesService,
private val permissionsManager: PermissionsManager, private val permissionsManager: PermissionsManager,
) : ViewModel() { ) : ViewModel() {
val grid = uiSettings.gridSettings
val columnCount = dataStore.data.map { it.grid.columnCount }
fun setColumnCount(columnCount: Int) { fun setColumnCount(columnCount: Int) {
viewModelScope.launch { uiSettings.setGridColumnCount(columnCount)
dataStore.updateData {
it.toBuilder()
.setGrid(it.grid.toBuilder().setColumnCount(columnCount))
.build()
}
}
} }
val iconSize = dataStore.data.map { it.grid.iconSize }
fun setIconSize(iconSize: Int) { fun setIconSize(iconSize: Int) {
viewModelScope.launch { uiSettings.setGridIconSize(iconSize)
dataStore.updateData {
it.toBuilder()
.setGrid(it.grid.toBuilder().setIconSize(iconSize))
.build()
}
}
} }
val showLabels = dataStore.data.map { it.grid.showLabels }
fun setShowLabels(showLabels: Boolean) { fun setShowLabels(showLabels: Boolean) {
viewModelScope.launch { uiSettings.setGridShowLabels(showLabels)
dataStore.updateData {
it.toBuilder()
.setGrid(it.grid.toBuilder().setShowLabels(showLabels))
.build()
}
}
} }
val iconShape = dataStore.data.map { it.icons.shape } val iconShape = uiSettings.iconShape
fun setIconShape(iconShape: Settings.IconSettings.IconShape) { fun setIconShape(iconShape: IconShape) {
viewModelScope.launch { uiSettings.setIconShape(iconShape)
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons.toBuilder()
.setShape(iconShape)
)
.build()
}
}
} }
val adaptifyLegacyIcons = dataStore.data.map { it.icons.adaptify } val icons = iconSettings
fun setAdaptifyLegacyIcons(adaptify: Boolean) { fun setAdaptifyLegacyIcons(adaptify: Boolean) {
viewModelScope.launch { iconSettings.setAdaptifyLegacyIcons(adaptify)
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons.toBuilder()
.setAdaptify(adaptify)
)
.build()
}
}
} }
val themedIcons = dataStore.data.map { it.icons.themedIcons }
fun setThemedIcons(themedIcons: Boolean) { fun setThemedIcons(themedIcons: Boolean) {
viewModelScope.launch { iconSettings.setThemedIcons(themedIcons)
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons.toBuilder()
.setThemedIcons(themedIcons)
)
.build()
}
}
} }
val forceThemedIcons = dataStore.data.map { it.icons.forceThemed }
fun setForceThemedIcons(forceThemedIcons: Boolean) { fun setForceThemedIcons(forceThemedIcons: Boolean) {
viewModelScope.launch { iconSettings.setForceThemedIcons(forceThemedIcons)
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons.toBuilder()
.setForceThemed(forceThemedIcons)
)
.build()
}
}
} }
val installedIconPacks: Flow<List<IconPack>> = iconService.getInstalledIconPacks().map { val installedIconPacks: Flow<List<IconPack>> = iconService.getInstalledIconPacks().map {
@ -132,33 +72,12 @@ class IconsSettingsScreenVM(
) + it ) + it
} }
val iconPackThemed = dataStore.data.map { it.icons.iconPackThemed }
fun setIconPackThemed(iconPackThemed: Boolean) { fun setIconPackThemed(iconPackThemed: Boolean) {
viewModelScope.launch { iconSettings.setIconPackThemed(iconPackThemed)
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons
.toBuilder()
.setIconPackThemed(iconPackThemed)
)
.build()
}
}
} }
val iconPack = dataStore.data.map { it.icons.iconPack } fun setIconPack(iconPack: String?) {
fun setIconPack(iconPack: String) { iconSettings.setIconPack(iconPack?.takeIf { it.isNotBlank() })
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setIcons(
it.icons.toBuilder()
.setIconPack(iconPack)
)
.build()
}
}
} }
val hasNotificationsPermission = permissionsManager.hasPermission(PermissionGroup.Notifications) val hasNotificationsPermission = permissionsManager.hasPermission(PermissionGroup.Notifications)
@ -193,10 +112,10 @@ class IconsSettingsScreenVM(
} }
fun getPreviewIcons(size: Int): Flow<List<LauncherIcon?>> { fun getPreviewIcons(size: Int): Flow<List<LauncherIcon?>> {
return columnCount.flatMapLatest { cols -> return grid.flatMapLatest { grid ->
favoritesService.getFavorites( favoritesService.getFavorites(
includeTypes = listOf("app"), includeTypes = listOf("app"),
limit = cols, limit = grid.columnCount,
manuallySorted = true, manuallySorted = true,
automaticallySorted = true, automaticallySorted = true,
frequentlyUsed = true, frequentlyUsed = true,
@ -214,11 +133,12 @@ class IconsSettingsScreenVM(
val Factory = viewModelFactory { val Factory = viewModelFactory {
initializer { initializer {
IconsSettingsScreenVM( IconsSettingsScreenVM(
dataStore = get(), uiSettings = get(),
iconService = get(), iconService = get(),
permissionsManager = get(), permissionsManager = get(),
favoritesService = get(), favoritesService = get(),
badgeSettings = get(), badgeSettings = get(),
iconSettings = get(),
) )
} }
} }

View File

@ -11,13 +11,12 @@ import de.mm20.launcher2.ktx.normalize
import de.mm20.launcher2.music.MusicService import de.mm20.launcher2.music.MusicService
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager 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 de.mm20.launcher2.search.AppProfile
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
@ -29,7 +28,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
private val musicService: MusicService by inject() private val musicService: MusicService by inject()
private val appRepository: AppRepository by inject() private val appRepository: AppRepository by inject()
private val iconService: IconService by inject() private val iconService: IconService by inject()
private val dataStore: LauncherDataStore by inject()
private val mediaSettings: MediaSettings by inject()
val hasPermission = val hasPermission =
permissionsManager.hasPermission(PermissionGroup.Notifications) permissionsManager.hasPermission(PermissionGroup.Notifications)
@ -52,9 +53,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
val musicApps = musicService.getInstalledPlayerPackages() val musicApps = musicService.getInstalledPlayerPackages()
val allApps = appRepository.findMany().first().filter { it.profile == AppProfile.Personal } val allApps = appRepository.findMany().first().filter { it.profile == AppProfile.Personal }
.distinctBy { it.componentName.packageName } .distinctBy { it.componentName.packageName }
val settings = dataStore.data.map { it.musicWidget }.first() val settings = mediaSettings.first()
val allowList = settings.allowListList val allowList = settings.allowList
val denyList = settings.denyListList val denyList = settings.denyList
appList.value = allApps.map { appList.value = allApps.map {
AppListItem( AppListItem(
@ -92,19 +93,7 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
denyList.add(app.packageName) denyList.add(app.packageName)
} }
} }
viewModelScope.launch { mediaSettings.setLists(allowList.toSet(), denyList.toSet())
dataStore.updateData {
it.toBuilder()
.setMusicWidget(
it.musicWidget.toBuilder()
.clearAllowList()
.addAllAllowList(allowList)
.clearDenyList()
.addAllDenyList(denyList)
)
.build()
}
}
} }
} }

View File

@ -7,14 +7,14 @@ import android.net.Uri
import android.provider.Settings import android.provider.Settings
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.files.settings.FileSearchSettings
import de.mm20.launcher2.ktx.tryStartActivity import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.plugin.PluginPackage import de.mm20.launcher2.plugin.PluginPackage
import de.mm20.launcher2.plugin.PluginState import de.mm20.launcher2.plugin.PluginState
import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.plugin.PluginType
import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.plugins.PluginWithState 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.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -111,6 +111,6 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent {
val weatherProvider = weatherSettings.providerId val weatherProvider = weatherSettings.providerId
fun setWeatherProvider(providerId: String) { fun setWeatherProvider(providerId: String) {
weatherSettings.setProviderId(providerId) weatherSettings.setProvider(providerId)
} }
} }

View File

@ -13,7 +13,8 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel 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.R
import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.preferences.* import de.mm20.launcher2.ui.component.preferences.*
@ -232,8 +233,8 @@ fun SearchSettingsScreen() {
ListPreference( ListPreference(
title = stringResource(R.string.preference_search_result_ordering), title = stringResource(R.string.preference_search_result_ordering),
items = listOf( items = listOf(
stringResource(R.string.preference_search_result_ordering_alphabetic) to Settings.SearchResultOrderingSettings.Ordering.Alphabetic, stringResource(R.string.preference_search_result_ordering_alphabetic) to SearchResultOrder.Alphabetical,
stringResource(R.string.preference_search_result_ordering_weighted) to Settings.SearchResultOrderingSettings.Ordering.Weighted stringResource(R.string.preference_search_result_ordering_weighted) to SearchResultOrder.Weighted
), ),
value = searchResultOrdering, value = searchResultOrdering,
onValueChanged = { onValueChanged = {

View File

@ -5,10 +5,16 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.SearchResultOrder
import de.mm20.launcher2.preferences.Settings 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.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -16,35 +22,32 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class SearchSettingsScreenVM : ViewModel(), KoinComponent { 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() private val permissionsManager: PermissionsManager by inject()
val favorites = dataStore.data.map { it.favorites.enabled } val favorites = searchUiSettings.favorites
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setFavorites(favorites: Boolean) { fun setFavorites(favorites: Boolean) {
viewModelScope.launch { searchUiSettings.setFavorites(favorites)
dataStore.updateData {
it.toBuilder().setFavorites(
it.favorites.toBuilder().setEnabled(favorites)
).build()
}
}
} }
val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts) val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val contacts = dataStore.data.map { it.contactsSearch.enabled } val contacts = contactSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setContacts(contacts: Boolean) { fun setContacts(contacts: Boolean) {
viewModelScope.launch { contactSearchSettings.setEnabled(contacts)
dataStore.updateData {
it.toBuilder().setContactsSearch(
it.contactsSearch.toBuilder().setEnabled(contacts)
).build()
}
}
} }
fun requestContactsPermission(activity: AppCompatActivity) { fun requestContactsPermission(activity: AppCompatActivity) {
@ -53,133 +56,81 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar) val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val calendar = dataStore.data.map { it.calendarSearch.enabled } val calendar = calendarSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalendar(calendar: Boolean) { fun setCalendar(calendar: Boolean) {
viewModelScope.launch { calendarSearchSettings.setEnabled(calendar)
dataStore.updateData {
it.toBuilder().setCalendarSearch(
it.calendarSearch.toBuilder().setEnabled(calendar)
).build()
}
}
} }
fun requestCalendarPermission(activity: AppCompatActivity) { fun requestCalendarPermission(activity: AppCompatActivity) {
permissionsManager.requestPermission(activity, PermissionGroup.Calendar) permissionsManager.requestPermission(activity, PermissionGroup.Calendar)
} }
val calculator = dataStore.data.map { it.calculatorSearch.enabled } val calculator = calculatorSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCalculator(calculator: Boolean) { fun setCalculator(calculator: Boolean) {
viewModelScope.launch { calculatorSearchSettings.setEnabled(calculator)
dataStore.updateData {
it.toBuilder().setCalculatorSearch(
it.calculatorSearch.toBuilder().setEnabled(calculator)
).build()
}
}
} }
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled } val unitConverter = unitConverterSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setUnitConverter(unitConverter: Boolean) { fun setUnitConverter(unitConverter: Boolean) {
viewModelScope.launch { unitConverterSettings.setEnabled(unitConverter)
dataStore.updateData {
it.toBuilder().setUnitConverterSearch(
it.unitConverterSearch.toBuilder().setEnabled(unitConverter)
).build()
}
}
} }
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled } val wikipedia = wikipediaSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWikipedia(wikipedia: Boolean) { fun setWikipedia(wikipedia: Boolean) {
viewModelScope.launch { wikipediaSearchSettings.setEnabled(wikipedia)
dataStore.updateData {
it.toBuilder().setWikipediaSearch(
it.wikipediaSearch.toBuilder().setEnabled(wikipedia)
).build()
}
}
} }
val websites = dataStore.data.map { it.websiteSearch.enabled } val websites = websiteSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWebsites(websites: Boolean) { fun setWebsites(websites: Boolean) {
viewModelScope.launch { websiteSearchSettings.setEnabled(websites)
dataStore.updateData {
it.toBuilder().setWebsiteSearch(
it.websiteSearch.toBuilder().setEnabled(websites)
).build()
}
}
} }
val autoFocus = dataStore.data.map { it.searchBar.autoFocus } val autoFocus = searchUiSettings.openKeyboard
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAutoFocus(autoFocus: Boolean) { fun setAutoFocus(autoFocus: Boolean) {
viewModelScope.launch { searchUiSettings.setOpenKeyboard(autoFocus)
dataStore.updateData {
it.toBuilder().setSearchBar(
it.searchBar.toBuilder().setAutoFocus(autoFocus)
).build()
}
}
} }
val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter } val launchOnEnter = searchUiSettings.launchOnEnter
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setLaunchOnEnter(launchOnEnter: Boolean) { fun setLaunchOnEnter(launchOnEnter: Boolean) {
viewModelScope.launch { searchUiSettings.setLaunchOnEnter(launchOnEnter)
dataStore.updateData {
it.toBuilder().setSearchBar(
it.searchBar.toBuilder().setLaunchOnEnter(launchOnEnter)
).build()
}
}
} }
val hasAppShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts) val hasAppShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts)
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val appShortcuts = dataStore.data.map { it.appShortcutSearch.enabled } val appShortcuts = shortcutSearchSettings.enabled
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setAppShortcuts(appShortcuts: Boolean) { fun setAppShortcuts(appShortcuts: Boolean) {
viewModelScope.launch { shortcutSearchSettings.setEnabled(appShortcuts)
dataStore.updateData {
it.toBuilder().setAppShortcutSearch(
it.appShortcutSearch.toBuilder().setEnabled(appShortcuts)
).build()
}
}
} }
val searchResultOrdering = dataStore.data.map { it.resultOrdering.ordering } val searchResultOrdering = searchUiSettings.resultOrder
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setSearchResultOrdering(searchResultOrdering: Settings.SearchResultOrderingSettings.Ordering) {
viewModelScope.launch { fun setSearchResultOrdering(searchResultOrdering: SearchResultOrder) {
dataStore.updateData { searchUiSettings.setResultOrder(searchResultOrdering)
it.toBuilder().setResultOrdering(
it.resultOrdering.toBuilder().setOrdering(searchResultOrdering)
).build()
}
}
} }
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults } val reverseSearchResults = searchUiSettings.reversedResults
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setReverseSearchResults(reverseSearchResults: Boolean) { fun setReverseSearchResults(reverseSearchResults: Boolean) {
viewModelScope.launch { searchUiSettings.setReversedResults(reverseSearchResults)
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setReverseSearchResults(reverseSearchResults))
.build()
}
}
} }
fun requestAppShortcutsPermission(activity: AppCompatActivity) { fun requestAppShortcutsPermission(activity: AppCompatActivity) {

View File

@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.unitconverter
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -12,35 +12,17 @@ import org.koin.core.component.inject
class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent { 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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setUnitConverter(unitConverter: Boolean) { fun setUnitConverter(unitConverter: Boolean) {
viewModelScope.launch { settings.setEnabled(unitConverter)
dataStore.updateData {
it.toBuilder()
.setUnitConverterSearch(
it.unitConverterSearch.toBuilder()
.setEnabled(unitConverter)
)
.build()
}
}
} }
val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies } val currencyConverter = settings.currencies
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setCurrencyConverter(currencyConverter: Boolean) { fun setCurrencyConverter(currencyConverter: Boolean) {
viewModelScope.launch { settings.setCurrencies(currencyConverter)
dataStore.updateData {
it.toBuilder()
.setUnitConverterSearch(
it.unitConverterSearch.toBuilder()
.setCurrencies(currencyConverter)
)
.build()
}
}
} }
} }

View File

@ -24,7 +24,6 @@ import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog
import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.Banner
import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.MissingPermissionBanner
import de.mm20.launcher2.ui.component.preferences.* import de.mm20.launcher2.ui.component.preferences.*
import de.mm20.launcher2.weather.WeatherLocation
import de.mm20.launcher2.weather.WeatherProviderInfo import de.mm20.launcher2.weather.WeatherProviderInfo
@Composable @Composable

View File

@ -1,26 +1,18 @@
package de.mm20.launcher2.ui.settings.weather package de.mm20.launcher2.ui.settings.weather
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.permissions.PermissionGroup import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.plugins.PluginService import de.mm20.launcher2.plugins.PluginService
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.weather.WeatherSettings
import de.mm20.launcher2.weather.WeatherLocation
import de.mm20.launcher2.weather.WeatherProviderInfo
import de.mm20.launcher2.weather.WeatherRepository import de.mm20.launcher2.weather.WeatherRepository
import de.mm20.launcher2.weather.settings.WeatherSettings
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.SharingStarted 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.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
@ -30,29 +22,22 @@ class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
private val weatherSettings: WeatherSettings by inject() private val weatherSettings: WeatherSettings by inject()
private val pluginService: PluginService by inject() private val pluginService: PluginService by inject()
private val permissionsManager: PermissionsManager by inject() private val permissionsManager: PermissionsManager by inject()
private val dataStore: LauncherDataStore by inject()
val availableProviders = repository.getProviders() val availableProviders = repository.getProviders()
val weatherProvider = weatherSettings.providerId val weatherProvider = weatherSettings.providerId
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWeatherProvider(provider: String) { fun setWeatherProvider(provider: String) {
weatherSettings.setProviderId(provider) weatherSettings.setProvider(provider)
} }
val weatherProviderPluginState = weatherProvider.flatMapLatest { val weatherProviderPluginState = weatherProvider.flatMapLatest {
it?.let { pluginService.getPluginWithState(it) } ?: flowOf(null) it?.let { pluginService.getPluginWithState(it) } ?: flowOf(null)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
val imperialUnits = dataStore.data.map { it.weather.imperialUnits } val imperialUnits = weatherSettings.imperialUnits
fun setImperialUnits(imperialUnits: Boolean) { fun setImperialUnits(imperialUnits: Boolean) {
viewModelScope.launch { weatherSettings.setImperialUnits(imperialUnits)
dataStore.updateData {
it.toBuilder()
.setWeather(it.weather.toBuilder().setImperialUnits(imperialUnits))
.build()
}
}
} }
val autoLocation = weatherSettings.autoLocation val autoLocation = weatherSettings.autoLocation

View File

@ -24,20 +24,10 @@ fun WikipediaSettingsScreen() {
viewModel.setWikipedia(it) 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() val customUrl by viewModel.customUrl.collectAsState()
TextPreference( TextPreference(
title = stringResource(R.string.preference_wikipedia_customurl), title = stringResource(R.string.preference_wikipedia_customurl),
value = customUrl, value = customUrl ?: "",
placeholder = stringResource(id = R.string.wikipedia_url), placeholder = stringResource(id = R.string.wikipedia_url),
summary = customUrl.takeIf { !it.isNullOrBlank() } summary = customUrl.takeIf { !it.isNullOrBlank() }
?: stringResource(id = R.string.wikipedia_url), ?: stringResource(id = R.string.wikipedia_url),

View File

@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.wikipedia
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope 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.SharingStarted
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@ -11,50 +11,17 @@ import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
class WikipediaSettingsScreenVM: ViewModel(), KoinComponent { 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) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
fun setWikipedia(wikipedia: Boolean) { fun setWikipedia(wikipedia: Boolean) {
viewModelScope.launch { wikipediaSearchSettings.setEnabled(wikipedia)
dataStore.updateData {
it.toBuilder()
.setWikipediaSearch(
it.wikipediaSearch.toBuilder()
.setEnabled(wikipedia)
)
.build()
}
}
} }
val images = dataStore.data.map { it.wikipediaSearch.images } val customUrl = wikipediaSearchSettings.customUrl
.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 }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "") .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
fun setCustomUrl(customUrl: String) { fun setCustomUrl(customUrl: String) {
viewModelScope.launch { wikipediaSearchSettings.setCustomUrl(customUrl)
dataStore.updateData {
it.toBuilder()
.setWikipediaSearch(
it.wikipediaSearch.toBuilder()
.setCustomUrl(customUrl)
)
.build()
}
}
} }
} }

View File

@ -1,23 +1,18 @@
package de.mm20.launcher2.ui.theme package de.mm20.launcher2.ui.theme
import android.content.Context import android.content.Context
import android.os.Build
import android.util.Log
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.coerceAtMost import androidx.compose.ui.unit.coerceAtMost
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.mm20.launcher2.preferences.LauncherDataStore import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.Settings.AppearanceSettings import de.mm20.launcher2.preferences.ui.UiSettings
import de.mm20.launcher2.themes.DefaultThemeId
import de.mm20.launcher2.themes.Theme
import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.ThemeRepository
import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalDarkTheme
import de.mm20.launcher2.ui.theme.colorscheme.* 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.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.koin.androidx.compose.inject import org.koin.androidx.compose.inject
import java.util.UUID import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref
@Composable @Composable
@ -35,33 +30,31 @@ fun LauncherTheme(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val dataStore: LauncherDataStore by inject() val uiSettings: UiSettings by inject()
val themeRepository: ThemeRepository by inject() val themeRepository: ThemeRepository by inject()
val theme by remember { val theme by remember {
dataStore.data.map { uiSettings.theme.flatMapLatest {
it.appearance.themeId.takeIf { it.isNotEmpty() }?.let {
UUID.fromString(it)
}
}.flatMapLatest {
themeRepository.getThemeOrDefault(it) themeRepository.getThemeOrDefault(it)
} }
}.collectAsState(themeRepository.getDefaultTheme()) }.collectAsState(themeRepository.getDefaultTheme())
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState( val colorSchemePref by remember { uiSettings.colorScheme }.collectAsState(
AppearanceSettings.Theme.System ColorSchemePref.System
) )
val darkTheme = val darkTheme =
themePreference == AppearanceSettings.Theme.Dark || themePreference == AppearanceSettings.Theme.System && isSystemInDarkTheme() colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme()
val cornerRadius by remember { val cornerRadius by remember {
dataStore.data.map { it.cards.radius.dp } uiSettings.cardStyle.map {
it.cornerRadius.dp
}
}.collectAsState(8.dp) }.collectAsState(8.dp)
val baseShape by remember { val baseShape by remember {
dataStore.data.map { uiSettings.cardStyle.map {
when (it.cards.shape) { when (it.shape) {
Settings.CardSettings.Shape.Cut -> CutCornerShape(0f) SurfaceShape.Cut -> CutCornerShape(0f)
else -> RoundedCornerShape(0f) else -> RoundedCornerShape(0f)
} }
} }
@ -73,8 +66,8 @@ fun LauncherTheme(
lightColorSchemeOf(theme) lightColorSchemeOf(theme)
} }
val font by remember { dataStore.data.map { it.appearance.font } }.collectAsState( val font by remember { uiSettings.font }.collectAsState(
AppearanceSettings.Font.Outfit Font.Outfit
) )
val typography = remember(font) { 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) { return when (font) {
AppearanceSettings.Font.SystemDefault -> getDeviceDefaultTypography(context) Font.System -> getDeviceDefaultTypography(context)
else -> DefaultTypography else -> DefaultTypography
} }
} }

View File

@ -8,7 +8,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat 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.CorePalette
import de.mm20.launcher2.themes.DefaultDarkColorScheme import de.mm20.launcher2.themes.DefaultDarkColorScheme
import de.mm20.launcher2.themes.DefaultLightColorScheme import de.mm20.launcher2.themes.DefaultLightColorScheme
@ -102,7 +102,7 @@ fun systemCorePalette(): CorePalette<Int> {
} }
} }
fun CustomColorScheme(colors: Settings.AppearanceSettings.CustomColors.Scheme): ColorScheme { fun CustomColorScheme(colors: LegacySettings.AppearanceSettings.CustomColors.Scheme): ColorScheme {
return ColorScheme( return ColorScheme(
primary = Color(colors.primary), primary = Color(colors.primary),
onPrimary = Color(colors.onPrimary), onPrimary = Color(colors.onPrimary),

View File

@ -4,6 +4,7 @@ plugins {
alias(libs.plugins.android.library) alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
alias(libs.plugins.protobuf) alias(libs.plugins.protobuf)
alias(libs.plugins.kotlin.plugin.serialization)
} }
android { android {
@ -57,6 +58,7 @@ protobuf {
dependencies { dependencies {
implementation(libs.kotlin.stdlib) implementation(libs.kotlin.stdlib)
implementation(libs.kotlinx.serialization.json)
implementation(libs.androidx.core) implementation(libs.androidx.core)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
api(libs.androidx.datastore) api(libs.androidx.datastore)

View File

@ -1,7 +1,7 @@
-keepclassmembers class de.mm20.launcher2.preferences.Settings { -keepclassmembers class de.mm20.launcher2.preferences.LegacySettings {
<fields>; <fields>;
} }
-keepclassmembers class de.mm20.launcher2.preferences.Settings$* { -keepclassmembers class de.mm20.launcher2.preferences.LegacySettings$* {
<fields>; <fields>;
} }

View File

@ -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<Settings>
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<DataMigration<Settings>> {
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(),
)
}

View File

@ -2,58 +2,58 @@ package de.mm20.launcher2.preferences
import android.content.Context import android.content.Context
import de.mm20.launcher2.ktx.isAtLeastApiLevel 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 de.mm20.launcher2.preferences.ktx.toSettingsColorsScheme
import scheme.Scheme import scheme.Scheme
import java.util.UUID import java.util.UUID
fun createFactorySettings(context: Context): Settings { fun createFactorySettings(context: Context): LegacySettings {
return Settings.newBuilder() return LegacySettings.newBuilder()
.setAppearance( .setAppearance(
Settings.AppearanceSettings LegacySettings.AppearanceSettings
.newBuilder() .newBuilder()
.setTheme(Settings.AppearanceSettings.Theme.System) .setTheme(LegacySettings.AppearanceSettings.Theme.System)
.setDimWallpaper(false) .setDimWallpaper(false)
.setBlurWallpaper(true) .setBlurWallpaper(true)
.setBlurWallpaperRadius(32) .setBlurWallpaperRadius(32)
.setThemeId(UUID(0L, 0L).toString()) .setThemeId(UUID(0L, 0L).toString())
.setFont(Settings.AppearanceSettings.Font.Outfit) .setFont(LegacySettings.AppearanceSettings.Font.Outfit)
.build() .build()
) )
.setWeather( .setWeather(
Settings.WeatherSettings LegacySettings.WeatherSettings
.newBuilder() .newBuilder()
.setProvider(Settings.WeatherSettings.WeatherProvider.MetNo) .setProvider(LegacySettings.WeatherSettings.WeatherProvider.MetNo)
.setImperialUnits(context.resources.getBoolean(R.bool.default_imperialUnits)) .setImperialUnits(context.resources.getBoolean(R.bool.default_imperialUnits))
.build() .build()
) )
.setMusicWidget( .setMusicWidget(
Settings.MusicWidgetSettings LegacySettings.MusicWidgetSettings
.newBuilder() .newBuilder()
.build() .build()
) )
.setCalendarWidget( .setCalendarWidget(
Settings.CalendarWidgetSettings LegacySettings.CalendarWidgetSettings
.newBuilder() .newBuilder()
.setHideAlldayEvents(false) .setHideAlldayEvents(false)
) )
.setClockWidget( .setClockWidget(
Settings.ClockWidgetSettings LegacySettings.ClockWidgetSettings
.newBuilder() .newBuilder()
.setLayout(Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) .setLayout(LegacySettings.ClockWidgetSettings.ClockWidgetLayout.Vertical)
.setClockStyle(Settings.ClockWidgetSettings.ClockStyle.DigitalClock1) .setClockStyle(LegacySettings.ClockWidgetSettings.ClockStyle.DigitalClock1)
.setColor(Settings.ClockWidgetSettings.ClockWidgetColors.Auto) .setColor(LegacySettings.ClockWidgetSettings.ClockWidgetColors.Auto)
.setAlarmPart(true) .setAlarmPart(true)
.setBatteryPart(true) .setBatteryPart(true)
.setMusicPart(true) .setMusicPart(true)
.setDatePart(true) .setDatePart(true)
.setFavoritesPart(false) .setFavoritesPart(false)
.setFillHeight(true) .setFillHeight(true)
.setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) .setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
.build() .build()
) )
.setFavorites( .setFavorites(
Settings.FavoritesSettings LegacySettings.FavoritesSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
.setFrequentlyUsed(true) .setFrequentlyUsed(true)
@ -61,7 +61,7 @@ fun createFactorySettings(context: Context): Settings {
.setEditButton(true) .setEditButton(true)
) )
.setFileSearch( .setFileSearch(
Settings.FilesSearchSettings LegacySettings.FilesSearchSettings
.newBuilder() .newBuilder()
.setLocalFiles(true) .setLocalFiles(true)
.setNextcloud(false) .setNextcloud(false)
@ -70,50 +70,50 @@ fun createFactorySettings(context: Context): Settings {
.setNextcloud(false) .setNextcloud(false)
) )
.setContactsSearch( .setContactsSearch(
Settings.ContactsSearchSettings LegacySettings.ContactsSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
) )
.setCalendarSearch( .setCalendarSearch(
Settings.CalendarSearchSettings LegacySettings.CalendarSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
) )
.setAppShortcutSearch( .setAppShortcutSearch(
Settings.AppShortcutSearchSettings LegacySettings.AppShortcutSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
) )
.setCalculatorSearch( .setCalculatorSearch(
Settings.CalculatorSearchSettings LegacySettings.CalculatorSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
) )
.setUnitConverterSearch( .setUnitConverterSearch(
Settings.UnitConverterSearchSettings LegacySettings.UnitConverterSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
.setCurrencies(true) .setCurrencies(true)
) )
.setWikipediaSearch( .setWikipediaSearch(
Settings.WikipediaSearchSettings LegacySettings.WikipediaSearchSettings
.newBuilder() .newBuilder()
.setEnabled(false) .setEnabled(false)
.setImages(false) .setImages(false)
.setCustomUrl("") .setCustomUrl("")
) )
.setWebsiteSearch( .setWebsiteSearch(
Settings.WebsiteSearchSettings LegacySettings.WebsiteSearchSettings
.newBuilder() .newBuilder()
.setEnabled(false) .setEnabled(false)
) )
.setWebSearch( .setWebSearch(
Settings.WebSearchSettings LegacySettings.WebSearchSettings
.newBuilder() .newBuilder()
.setEnabled(true) .setEnabled(true)
) )
.setBadges( .setBadges(
Settings.BadgeSettings LegacySettings.BadgeSettings
.newBuilder() .newBuilder()
.setNotifications(true) .setNotifications(true)
.setCloudFiles(true) .setCloudFiles(true)
@ -121,15 +121,15 @@ fun createFactorySettings(context: Context): Settings {
.setSuspendedApps(true) .setSuspendedApps(true)
) )
.setGrid( .setGrid(
Settings.GridSettings.newBuilder() LegacySettings.GridSettings.newBuilder()
.setColumnCount(context.resources.getInteger(R.integer.config_columnCount)) .setColumnCount(context.resources.getInteger(R.integer.config_columnCount))
.setIconSize(48) .setIconSize(48)
.setShowLabels(true) .setShowLabels(true)
.build() .build()
) )
.setSearchBar( .setSearchBar(
Settings.SearchBarSettings.newBuilder() LegacySettings.SearchBarSettings.newBuilder()
.setSearchBarStyle(Settings.SearchBarSettings.SearchBarStyle.Transparent) .setSearchBarStyle(LegacySettings.SearchBarSettings.SearchBarStyle.Transparent)
.setAutoFocus(true) .setAutoFocus(true)
.setLaunchOnEnter(true) .setLaunchOnEnter(true)
.setColor(SearchBarColors.Auto) .setColor(SearchBarColors.Auto)
@ -137,67 +137,67 @@ fun createFactorySettings(context: Context): Settings {
.build() .build()
) )
.setIcons( .setIcons(
Settings.IconSettings.newBuilder() LegacySettings.IconSettings.newBuilder()
.setAdaptify(true) .setAdaptify(true)
.setShape(Settings.IconSettings.IconShape.PlatformDefault) .setShape(LegacySettings.IconSettings.IconShape.PlatformDefault)
.setThemedIcons(false) .setThemedIcons(false)
.setIconPack("") .setIconPack("")
.setIconPackThemed(true) .setIconPackThemed(true)
) )
.setEasterEgg(false) .setEasterEgg(false)
.setSystemBars( .setSystemBars(
Settings.SystemBarsSettings.newBuilder() LegacySettings.SystemBarsSettings.newBuilder()
.setNavBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto) .setNavBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto)
.setStatusBarColor(Settings.SystemBarsSettings.SystemBarColors.Auto) .setStatusBarColor(LegacySettings.SystemBarsSettings.SystemBarColors.Auto)
.setHideStatusBar(false) .setHideStatusBar(false)
.setHideNavBar(false) .setHideNavBar(false)
) )
.setCards( .setCards(
Settings.CardSettings.newBuilder() LegacySettings.CardSettings.newBuilder()
.setBorderWidth(0) .setBorderWidth(0)
.setRadius(12) .setRadius(12)
.setOpacity(1f) .setOpacity(1f)
) )
.setWidgets( .setWidgets(
Settings.WidgetSettings.newBuilder() LegacySettings.WidgetSettings.newBuilder()
.setEditButton(true) .setEditButton(true)
) )
.setLayout( .setLayout(
Settings.LayoutSettings.newBuilder() LegacySettings.LayoutSettings.newBuilder()
.setBaseLayout(Settings.LayoutSettings.Layout.PullDown) .setBaseLayout(LegacySettings.LayoutSettings.Layout.PullDown)
.setBottomSearchBar(false) .setBottomSearchBar(false)
.setReverseSearchResults(false) .setReverseSearchResults(false)
.setFixedRotation(false) .setFixedRotation(false)
) )
.setGestures( .setGestures(
Settings.GestureSettings.newBuilder() LegacySettings.GestureSettings.newBuilder()
.setDoubleTap( .setDoubleTap(
if (isAtLeastApiLevel(28)) { if (isAtLeastApiLevel(28)) {
Settings.GestureSettings.GestureAction.LockScreen LegacySettings.GestureSettings.GestureAction.LockScreen
} else { } else {
Settings.GestureSettings.GestureAction.None LegacySettings.GestureSettings.GestureAction.None
}) })
.setLongPress(Settings.GestureSettings.GestureAction.None) .setLongPress(LegacySettings.GestureSettings.GestureAction.None)
.setSwipeDown(Settings.GestureSettings.GestureAction.OpenNotificationDrawer) .setSwipeDown(LegacySettings.GestureSettings.GestureAction.OpenNotificationDrawer)
.setSwipeLeft(Settings.GestureSettings.GestureAction.None) .setSwipeLeft(LegacySettings.GestureSettings.GestureAction.None)
.setSwipeRight(Settings.GestureSettings.GestureAction.None) .setSwipeRight(LegacySettings.GestureSettings.GestureAction.None)
) )
.setResultOrdering( .setResultOrdering(
Settings.SearchResultOrderingSettings.newBuilder() LegacySettings.SearchResultOrderingSettings.newBuilder()
.setOrdering(Settings.SearchResultOrderingSettings.Ordering.Weighted) .setOrdering(LegacySettings.SearchResultOrderingSettings.Ordering.Weighted)
.setWeightFactor(Settings.SearchResultOrderingSettings.WeightFactor.Default) .setWeightFactor(LegacySettings.SearchResultOrderingSettings.WeightFactor.Default)
) )
.setAnimations( .setAnimations(
Settings.AnimationSettings.newBuilder() LegacySettings.AnimationSettings.newBuilder()
.setCharging(true) .setCharging(true)
) )
.build() .build()
} }
internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.BaseColors internal val DefaultCustomColorsBase: LegacySettings.AppearanceSettings.CustomColors.BaseColors
get() { get() {
val scheme = Scheme.light(0xFFACE330.toInt()) val scheme = Scheme.light(0xFFACE330.toInt())
return Settings.AppearanceSettings.CustomColors.BaseColors.newBuilder() return LegacySettings.AppearanceSettings.CustomColors.BaseColors.newBuilder()
.setAccent1(scheme.primary) .setAccent1(scheme.primary)
.setAccent2(scheme.secondary) .setAccent2(scheme.secondary)
.setAccent3(scheme.tertiary) .setAccent3(scheme.tertiary)
@ -207,13 +207,13 @@ internal val DefaultCustomColorsBase: Settings.AppearanceSettings.CustomColors.B
.build() .build()
} }
internal val DefaultLightCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme internal val DefaultLightCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme
get() { get() {
val scheme = Scheme.light(0xFFACE330.toInt()) val scheme = Scheme.light(0xFFACE330.toInt())
return scheme.toSettingsColorsScheme() return scheme.toSettingsColorsScheme()
} }
internal val DefaultDarkCustomColorScheme: Settings.AppearanceSettings.CustomColors.Scheme internal val DefaultDarkCustomColorScheme: LegacySettings.AppearanceSettings.CustomColors.Scheme
get() { get() {
val scheme = Scheme.dark(0xFFACE330.toInt()) val scheme = Scheme.dark(0xFFACE330.toInt())
return scheme.toSettingsColorsScheme() return scheme.toSettingsColorsScheme()

View File

@ -14,7 +14,7 @@ import java.io.File
internal class LauncherStoreBackupComponent( internal class LauncherStoreBackupComponent(
private val context: Context, private val context: Context,
private val dataStore: LauncherDataStore private val dataStore: LegacyDataStore
): Backupable { ): Backupable {
override suspend fun backup(toDir: File) { override suspend fun backup(toDir: File) {
dataStore.export(toDir) 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 scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val backupDataStore = DataStoreFactory.create( val backupDataStore = DataStoreFactory.create(
serializer = SettingsSerializer, serializer = LegacySettingsSerializer,
produceFile = { produceFile = {
File(toDir, "settings") 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 scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val backupDataStore = DataStoreFactory.create( val backupDataStore = DataStoreFactory.create(
serializer = SettingsSerializer, serializer = LegacySettingsSerializer,
migrations = getMigrations(context), migrations = getMigrations(context),
corruptionHandler = ReplaceFileCorruptionHandler { corruptionHandler = ReplaceFileCorruptionHandler {
CrashReporter.logException(it) CrashReporter.logException(it)
Settings.getDefaultInstance() LegacySettings.getDefaultInstance()
}, },
produceFile = { produceFile = {
File(fromDir, "settings") File(fromDir, "settings")

View File

@ -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<LauncherSettingsData>(
context,
fileName = "settings.json",
serializer = LauncherSettingsDataSerializer,
migrations = listOf(Migration1(legacyDataStore)),
) {
val data
get() = context.dataStore.data
fun update(block: (LauncherSettingsData) -> LauncherSettingsData) {
updateData(block)
}
}

View File

@ -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<String> = emptySet(),
val mediaDenyList: Set<String> = 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<String> = 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<String, ProviderSettings> = 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,
)

View File

@ -1,8 +1,7 @@
package de.mm20.launcher2.files.settings package de.mm20.launcher2.preferences
import androidx.datastore.core.CorruptionException import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer import androidx.datastore.core.Serializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerializationException import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
@ -11,27 +10,17 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@Serializable internal object LauncherSettingsDataSerializer : Serializer<LauncherSettingsData> {
data class FileSearchSettingsData(
val localFiles: Boolean = true,
val gdriveFiles: Boolean = false,
val nextcloudFiles: Boolean = false,
val owncloudFiles: Boolean = false,
val plugins: Set<String> = emptySet(),
val schemaVersion: Int = 1,
)
internal object FileSearchSettingsDataSerializer : Serializer<FileSearchSettingsData> {
internal val json = Json { internal val json = Json {
ignoreUnknownKeys = true ignoreUnknownKeys = true
encodeDefaults = true encodeDefaults = true
} }
override val defaultValue: FileSearchSettingsData override val defaultValue: LauncherSettingsData
get() = FileSearchSettingsData(schemaVersion = 0) get() = LauncherSettingsData(schemaVersion = 0)
override suspend fun readFrom(input: InputStream): FileSearchSettingsData { override suspend fun readFrom(input: InputStream): LauncherSettingsData {
try { try {
return json.decodeFromStream(input) return json.decodeFromStream(input)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
@ -43,7 +32,7 @@ internal object FileSearchSettingsDataSerializer : Serializer<FileSearchSettings
} }
} }
override suspend fun writeTo(t: FileSearchSettingsData, output: OutputStream) { override suspend fun writeTo(t: LauncherSettingsData, output: OutputStream) {
json.encodeToStream(t, output) json.encodeToStream(t, output)
} }
} }

View File

@ -0,0 +1,52 @@
package de.mm20.launcher2.preferences
import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataMigration
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStore
import com.google.protobuf.InvalidProtocolBufferException
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.preferences.migrations.*
import java.io.InputStream
import java.io.OutputStream
internal typealias LegacyDataStore = DataStore<LegacySettings>
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<DataMigration<LegacySettings>> {
return listOf()
}
object LegacySettingsSerializer : Serializer<LegacySettings> {
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)
}
}

View File

@ -1,11 +1,51 @@
package de.mm20.launcher2.preferences package de.mm20.launcher2.preferences
import de.mm20.launcher2.backup.Backupable 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.android.ext.koin.androidContext
import org.koin.core.qualifier.named import org.koin.core.qualifier.named
import org.koin.dsl.module import org.koin.dsl.module
val preferencesModule = module { val preferencesModule = module {
single { androidContext().dataStore } single { androidContext().legacyDataStore }
factory<Backupable>(named<LauncherDataStore>()) { LauncherStoreBackupComponent(androidContext(), get()) } factory<Backupable>(named<LegacyDataStore>()) { LauncherStoreBackupComponent(androidContext(), get()) }
single { LauncherDataStore(androidContext(), get()) }
factory<Backupable>(named<LauncherDataStore>()) { get<LauncherDataStore>() }
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()) }
} }

View File

@ -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<Settings> {
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)
}
}

View File

@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.ktx package de.mm20.launcher2.preferences.ktx
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import scheme.Scheme import scheme.Scheme
fun Scheme.toSettingsColorsScheme(): Settings.AppearanceSettings.CustomColors.Scheme { fun Scheme.toSettingsColorsScheme(): LegacySettings.AppearanceSettings.CustomColors.Scheme {
val scheme = this val scheme = this
return Settings.AppearanceSettings.CustomColors.Scheme.newBuilder() return LegacySettings.AppearanceSettings.CustomColors.Scheme.newBuilder()
.setPrimary(scheme.primary) .setPrimary(scheme.primary)
.setSurfaceTint(scheme.primary) .setSurfaceTint(scheme.primary)
.setOnPrimary(scheme.onPrimary) .setOnPrimary(scheme.onPrimary)

View File

@ -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<String>,
val denyList: Set<String>,
)
class MediaSettings internal constructor(
private val launcherDataStore: LauncherDataStore
) : Flow<MediaSettingsData> by (launcherDataStore.data.map {
MediaSettingsData(
it.mediaAllowList,
it.mediaDenyList
)
}) {
val allowList
get() = launcherDataStore.data.map { it.mediaAllowList }
fun setLists(allowList: Set<String>, denyList: Set<String>) {
launcherDataStore.update {
it.copy(mediaAllowList = allowList)
}
}
val denyList
get() = launcherDataStore.data.map { it.mediaDenyList }
}

View File

@ -4,22 +4,22 @@ import android.content.Context
import android.util.Log import android.util.Log
import androidx.datastore.core.DataMigration import androidx.datastore.core.DataMigration
import de.mm20.launcher2.preferences.SchemaVersion import de.mm20.launcher2.preferences.SchemaVersion
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.createFactorySettings import de.mm20.launcher2.preferences.createFactorySettings
class FactorySettingsMigration(private val context: Context): DataMigration<Settings> { class FactorySettingsMigration(private val context: Context): DataMigration<LegacySettings> {
override suspend fun cleanUp() { 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", "Initializing user settings…")
Log.d("MM20", "Done") Log.d("MM20", "Done")
val defaults = createFactorySettings(context) val defaults = createFactorySettings(context)
return defaults.toBuilder().setVersion(SchemaVersion).build() return defaults.toBuilder().setVersion(SchemaVersion).build()
} }
override suspend fun shouldMigrate(currentData: Settings): Boolean { override suspend fun shouldMigrate(currentData: LegacySettings): Boolean {
return currentData.version == 0 return currentData.version == 0
} }
} }

View File

@ -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<LauncherSettingsData> {
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
}
}
}

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setAppearance(
builder.appearance.toBuilder() builder.appearance.toBuilder()
.setCustomColors( .setCustomColors(

View File

@ -1,12 +1,12 @@
package de.mm20.launcher2.preferences.migrations package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.ktx.isAtLeastApiLevel
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.Settings.GestureSettings import de.mm20.launcher2.preferences.LegacySettings.GestureSettings
import de.mm20.launcher2.preferences.Settings.LayoutSettings import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings
class Migration_11_12: VersionedMigration(11, 12) { 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 val oldLayout = builder.appearance.layout
when(oldLayout) { when(oldLayout) {
LayoutSettings.Layout.Pager -> { LayoutSettings.Layout.Pager -> {

View File

@ -1,10 +1,10 @@
package de.mm20.launcher2.preferences.migrations package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.WeightFactor
class Migration_12_13: VersionedMigration(12, 13) { 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 return builder
.setClockWidget( .setClockWidget(
builder.clockWidget.toBuilder() builder.clockWidget.toBuilder()

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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 return builder
.setIcons( .setIcons(
builder.icons.toBuilder() builder.icons.toBuilder()

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setSearchBar(
builder.searchBar.toBuilder() builder.searchBar.toBuilder()
.setHiddenItemsButton(true) .setHiddenItemsButton(true)

View File

@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.migrations package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.CustomColors.Scheme import de.mm20.launcher2.preferences.LegacySettings.AppearanceSettings.CustomColors.Scheme
import palettes.TonalPalette import palettes.TonalPalette
class Migration_15_16 : VersionedMigration(15, 16) { 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( return builder.setAppearance(
builder.appearance.toBuilder() builder.appearance.toBuilder()
.setCustomColors( .setCustomColors(

View File

@ -1,16 +1,16 @@
package de.mm20.launcher2.preferences.migrations package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.preferences.LegacySettings
import java.util.UUID import java.util.UUID
class Migration_16_17: VersionedMigration(16, 17) { 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( return builder.setAppearance(
builder.appearance.toBuilder() builder.appearance.toBuilder()
.setThemeId( .setThemeId(
when(builder.appearance.colorScheme) { when(builder.appearance.colorScheme) {
Settings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L) LegacySettings.AppearanceSettings.ColorScheme.BlackAndWhite -> UUID(0L, 1L)
Settings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L) LegacySettings.AppearanceSettings.ColorScheme.Custom -> UUID(1L, 1L)
else -> UUID(0L, 0L) else -> UUID(0L, 0L)
}.toString() }.toString()
) )

View File

@ -1,15 +1,15 @@
package de.mm20.launcher2.preferences.migrations 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) { 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 return builder
.setAppearance(builder.appearance.toBuilder() .setAppearance(builder.appearance.toBuilder()
.setBlurWallpaperRadius(32) .setBlurWallpaperRadius(32)
) )
.setClockWidget(builder.clockWidget.toBuilder() .setClockWidget(builder.clockWidget.toBuilder()
.setAlignment(Settings.ClockWidgetSettings.ClockWidgetAlignment.Bottom) .setAlignment(LegacySettings.ClockWidgetSettings.ClockWidgetAlignment.Bottom)
) )
} }
} }

View File

@ -1,10 +1,10 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setSystemBars(
builder.systemBars.toBuilder() builder.systemBars.toBuilder()
.setHideNavBar(false) .setHideNavBar(false)

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setClockWidget(
builder.clockWidget.toBuilder() builder.clockWidget.toBuilder()
.setAlarmPart(true) .setAlarmPart(true)

View File

@ -1,11 +1,11 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setAppShortcutSearch(
Settings.AppShortcutSearchSettings.newBuilder() LegacySettings.AppShortcutSearchSettings.newBuilder()
.setEnabled(true) .setEnabled(true)
) )
} }

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setGrid(
builder.grid.toBuilder() builder.grid.toBuilder()
.setIconSize(48) .setIconSize(48)

View File

@ -3,13 +3,14 @@ package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.DefaultCustomColorsBase import de.mm20.launcher2.preferences.DefaultCustomColorsBase
import de.mm20.launcher2.preferences.DefaultDarkCustomColorScheme import de.mm20.launcher2.preferences.DefaultDarkCustomColorScheme
import de.mm20.launcher2.preferences.DefaultLightCustomColorScheme 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) { 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( return builder.setAppearance(
builder.appearance.toBuilder() builder.appearance.toBuilder()
.setCustomColors(Settings.AppearanceSettings.CustomColors.newBuilder() .setCustomColors(
LegacySettings.AppearanceSettings.CustomColors.newBuilder()
.setAdvancedMode(false) .setAdvancedMode(false)
.setBaseColors(DefaultCustomColorsBase) .setBaseColors(DefaultCustomColorsBase)
.setLightScheme(DefaultLightCustomColorScheme) .setLightScheme(DefaultLightCustomColorScheme)

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setIcons(
builder.icons.toBuilder() builder.icons.toBuilder()
.setAdaptify(true) .setAdaptify(true)

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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( return builder.setAppearance(
builder.appearance.toBuilder() builder.appearance.toBuilder()
.setBlurWallpaper(true) .setBlurWallpaper(true)

View File

@ -1,9 +1,9 @@
package de.mm20.launcher2.preferences.migrations 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) { 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 return builder
.setClockWidget( .setClockWidget(
builder.clockWidget.toBuilder() builder.clockWidget.toBuilder()

Some files were not shown because too many files have changed in this diff Show More