Merge remote-tracking branch 'upstream/main' into feat/materialYouCompat
Signed-off-by: Guillermo Villafuerte <gvillafu@comunidad.unam.mx> # Conflicts: # app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt # app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt # core/preferences/src/main/java/de/mm20/launcher2/preferences/DataStore.kt # core/preferences/src/main/java/de/mm20/launcher2/preferences/Defaults.kt
This commit is contained in:
@@ -19,19 +19,17 @@ import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||
import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM
|
||||
import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler
|
||||
import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchColumn
|
||||
import de.mm20.launcher2.ui.launcher.search.SearchVM
|
||||
import de.mm20.launcher2.ui.launcher.searchbar.LauncherSearchBar
|
||||
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Composable
|
||||
fun AssistantScaffold(
|
||||
@@ -204,7 +202,7 @@ fun AssistantScaffold(
|
||||
showHiddenItemsButton = true,
|
||||
value = { value },
|
||||
onValueChange = { searchVM.search(it) },
|
||||
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == Settings.SearchBarSettings.SearchBarColors.Auto || searchBarColor == Settings.SearchBarSettings.SearchBarColors.Dark,
|
||||
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark,
|
||||
style = searchBarStyle,
|
||||
reverse = bottomSearchBar,
|
||||
onKeyboardActionGo = if (launchOnEnter) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package de.mm20.launcher2.ui.base
|
||||
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.IconShape
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.ui.CardStyle
|
||||
import de.mm20.launcher2.preferences.ui.GridSettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.ui.component.ProvideIconShape
|
||||
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||
import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled
|
||||
@@ -19,32 +21,28 @@ import org.koin.androidx.compose.inject
|
||||
fun ProvideSettings(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val dataStore: LauncherDataStore by inject()
|
||||
val settings: UiSettings by inject()
|
||||
val widgetRepository: WidgetRepository by inject()
|
||||
|
||||
val cardStyle by remember {
|
||||
dataStore.data.map { it.cards }.distinctUntilChanged()
|
||||
settings.cardStyle.distinctUntilChanged()
|
||||
}.collectAsState(
|
||||
Settings.CardSettings.getDefaultInstance()
|
||||
CardStyle()
|
||||
)
|
||||
val iconShape by remember {
|
||||
dataStore.data.map {
|
||||
if (it.easterEgg) Settings.IconSettings.IconShape.EasterEgg
|
||||
else it.icons.shape
|
||||
}.distinctUntilChanged()
|
||||
}.collectAsState(Settings.IconSettings.IconShape.Circle)
|
||||
settings.iconShape.distinctUntilChanged()
|
||||
}.collectAsState(IconShape.Circle)
|
||||
|
||||
val favoritesEnabled by remember {
|
||||
combine(
|
||||
widgetRepository.exists(FavoritesWidget.Type),
|
||||
dataStore.data.map { it.favorites.enabled },
|
||||
dataStore.data.map { it.clockWidget.favoritesPart },
|
||||
) { a, b, c -> a || b || c }.distinctUntilChanged()
|
||||
settings.favoritesEnabled,
|
||||
) { a, b -> a || b }.distinctUntilChanged()
|
||||
}.collectAsState(true)
|
||||
|
||||
val gridSettings by remember {
|
||||
dataStore.data.map { it.grid }.distinctUntilChanged()
|
||||
}.collectAsState(Settings.GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build())
|
||||
settings.gridSettings.distinctUntilChanged()
|
||||
}.collectAsState(GridSettings())
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalCardStyle provides cardStyle,
|
||||
|
||||
@@ -4,7 +4,8 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.data.customattrs.CustomAttributesRepository
|
||||
import de.mm20.launcher2.data.customattrs.utils.withCustomLabels
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.search.FavoritesSettings
|
||||
import de.mm20.launcher2.preferences.search.FavoritesSettingsData
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.data.Tag
|
||||
import de.mm20.launcher2.services.favorites.FavoritesService
|
||||
@@ -19,11 +20,11 @@ abstract class FavoritesVM : ViewModel(), KoinComponent {
|
||||
private val favoritesService: FavoritesService by inject()
|
||||
internal val widgetRepository: WidgetRepository by inject()
|
||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||
internal val dataStore: LauncherDataStore by inject()
|
||||
internal val settings: FavoritesSettings by inject()
|
||||
|
||||
val selectedTag = MutableStateFlow<String?>(null)
|
||||
|
||||
val showEditButton = dataStore.data.map { it.favorites.editButton }
|
||||
val showEditButton = settings.showEditButton
|
||||
abstract val tagsExpanded: Flow<Boolean>
|
||||
|
||||
val pinnedTags = favoritesService.getFavorites(
|
||||
@@ -36,24 +37,18 @@ abstract class FavoritesVM : ViewModel(), KoinComponent {
|
||||
|
||||
open val favorites: Flow<List<SavableSearchable>> = selectedTag.flatMapLatest { tag ->
|
||||
if (tag == null) {
|
||||
val columns = dataStore.data.map { it.grid.columnCount }
|
||||
val excludeCalendar = widgetRepository.exists(CalendarWidget.Type)
|
||||
val includeFrequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
|
||||
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
|
||||
|
||||
combine(
|
||||
listOf(
|
||||
columns,
|
||||
excludeCalendar,
|
||||
includeFrequentlyUsed,
|
||||
frequentlyUsedRows
|
||||
)
|
||||
) { it }.transformLatest {
|
||||
excludeCalendar,
|
||||
settings,
|
||||
) { (a, b) -> a as Boolean to b as FavoritesSettingsData }
|
||||
.transformLatest {
|
||||
|
||||
val columns = it[0] as Int
|
||||
val excludeCalendar = it[1] as Boolean
|
||||
val includeFrequentlyUsed = it[2] as Boolean
|
||||
val frequentlyUsedRows = it[3] as Int
|
||||
val columns = it.second.columns
|
||||
val excludeCalendar = it.first
|
||||
val includeFrequentlyUsed = it.second.frequentlyUsed
|
||||
val frequentlyUsedRows = it.second.frequentlyUsedRows
|
||||
|
||||
val pinned = favoritesService.getFavorites(
|
||||
excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"),
|
||||
|
||||
@@ -5,7 +5,8 @@ import android.net.Uri
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.ThemeDescriptor
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.themes.fromJson
|
||||
@@ -17,7 +18,7 @@ import org.koin.core.component.inject
|
||||
class ImportThemeSheetVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val theme = mutableStateOf<Theme?>(null)
|
||||
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)
|
||||
if (apply) {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder()
|
||||
.setThemeId(theme.id.toString())
|
||||
).build()
|
||||
}
|
||||
uiSettings.setTheme(ThemeDescriptor.Custom(theme.id.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchService
|
||||
import de.mm20.launcher2.search.toList
|
||||
@@ -15,15 +14,13 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class SearchablePickerVM: ViewModel(), KoinComponent {
|
||||
class SearchablePickerVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val searchService: SearchService by inject()
|
||||
private val iconService: IconService by inject()
|
||||
|
||||
@@ -41,15 +38,11 @@ class SearchablePickerVM: ViewModel(), KoinComponent {
|
||||
searchQuery = query
|
||||
searchJob?.cancel()
|
||||
searchJob = viewModelScope.launch {
|
||||
val settings = dataStore.data.first()
|
||||
searchService.search(
|
||||
query = query,
|
||||
shortcuts = settings.appShortcutSearch,
|
||||
contacts = settings.contactsSearch,
|
||||
calendars = settings.calendarSearch,
|
||||
).collectLatest {
|
||||
if (searchQuery != query) return@collectLatest
|
||||
items = withContext(Dispatchers.Default) {
|
||||
items = withContext(Dispatchers.Default) {
|
||||
it.toList().filterIsInstance<SavableSearchable>().sorted()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package de.mm20.launcher2.ui.common
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.mm20.launcher2.weather.WeatherLocation
|
||||
import de.mm20.launcher2.preferences.weather.WeatherLocation
|
||||
import de.mm20.launcher2.preferences.weather.WeatherSettings
|
||||
import de.mm20.launcher2.weather.WeatherRepository
|
||||
import de.mm20.launcher2.weather.settings.WeatherSettings
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
@@ -39,7 +39,7 @@ import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.layout.BottomReversed
|
||||
import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||
@@ -47,7 +47,7 @@ import de.mm20.launcher2.ui.locals.LocalCardStyle
|
||||
@Composable
|
||||
fun SearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
style: Settings.SearchBarSettings.SearchBarStyle,
|
||||
style: SearchBarStyle,
|
||||
level: SearchBarLevel,
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
@@ -78,7 +78,7 @@ fun SearchBar(
|
||||
}
|
||||
) {
|
||||
when {
|
||||
it == SearchBarLevel.Resting && style != Settings.SearchBarSettings.SearchBarStyle.Solid -> 0.dp
|
||||
it == SearchBarLevel.Resting && style != SearchBarStyle.Solid -> 0.dp
|
||||
it == SearchBarLevel.Raised -> 8.dp
|
||||
else -> 2.dp
|
||||
}
|
||||
@@ -98,7 +98,7 @@ fun SearchBar(
|
||||
}) {
|
||||
when {
|
||||
it == SearchBarLevel.Active -> LocalCardStyle.current.opacity
|
||||
style != Settings.SearchBarSettings.SearchBarStyle.Transparent -> 1f
|
||||
style != SearchBarStyle.Transparent -> 1f
|
||||
it == SearchBarLevel.Resting -> 0f
|
||||
else -> 1f
|
||||
}
|
||||
@@ -117,14 +117,14 @@ fun SearchBar(
|
||||
}
|
||||
}) {
|
||||
when {
|
||||
style != Settings.SearchBarSettings.SearchBarStyle.Transparent -> MaterialTheme.colorScheme.onSurface
|
||||
style != SearchBarStyle.Transparent -> MaterialTheme.colorScheme.onSurface
|
||||
it == SearchBarLevel.Resting -> if (darkColors) Color(0, 0, 0, 180) else Color.White
|
||||
else -> MaterialTheme.colorScheme.onSurface
|
||||
}
|
||||
}
|
||||
|
||||
val opacity by transition.animateFloat(label = "opacity") {
|
||||
if (style == Settings.SearchBarSettings.SearchBarStyle.Hidden && it == SearchBarLevel.Resting) 0f
|
||||
if (style == SearchBarStyle.Hidden && it == SearchBarLevel.Resting) 0f
|
||||
else 1f
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -57,7 +56,6 @@ import androidx.compose.ui.graphics.drawscope.withTransform
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.graphics.toAndroidRect
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Density
|
||||
@@ -80,7 +78,7 @@ import de.mm20.launcher2.icons.TintedClockLayer
|
||||
import de.mm20.launcher2.icons.TintedIconLayer
|
||||
import de.mm20.launcher2.icons.TransparentLayer
|
||||
import de.mm20.launcher2.ktx.drawWithColorFilter
|
||||
import de.mm20.launcher2.preferences.Settings.IconSettings.IconShape
|
||||
import de.mm20.launcher2.preferences.IconShape
|
||||
import de.mm20.launcher2.ui.base.LocalTime
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
@@ -495,7 +493,6 @@ fun getShape(iconShape: IconShape): Shape {
|
||||
IconShape.Teardrop -> TeardropShape
|
||||
IconShape.Pebble -> PebbleShape
|
||||
IconShape.EasterEgg -> EasterEggShape
|
||||
IconShape.UNRECOGNIZED -> CircleShape
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.compose.ui.hapticfeedback.HapticFeedback
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.zIndex
|
||||
import de.mm20.launcher2.ui.ktx.animateTo
|
||||
@@ -178,7 +177,6 @@ fun LazyVerticalDragAndDropGrid(
|
||||
columns,
|
||||
modifier.dragAndDrop(
|
||||
state,
|
||||
LocalLayoutDirection.current == LayoutDirection.Rtl,
|
||||
LocalHapticFeedback.current
|
||||
),
|
||||
state.gridState,
|
||||
@@ -208,7 +206,6 @@ fun LazyHorizontalDragAndDropGrid(
|
||||
rows,
|
||||
modifier.dragAndDrop(
|
||||
state,
|
||||
LocalLayoutDirection.current == LayoutDirection.Rtl,
|
||||
LocalHapticFeedback.current
|
||||
),
|
||||
state.gridState,
|
||||
@@ -224,7 +221,6 @@ fun LazyHorizontalDragAndDropGrid(
|
||||
|
||||
fun Modifier.dragAndDrop(
|
||||
state: LazyDragAndDropGridState,
|
||||
isRtl: Boolean,
|
||||
hapticFeedback: HapticFeedback
|
||||
) =
|
||||
this then pointerInput(null) {
|
||||
@@ -235,10 +231,7 @@ fun Modifier.dragAndDrop(
|
||||
onDragStart = { offset ->
|
||||
val draggedItem = state.gridState.layoutInfo.visibleItemsInfo.find {
|
||||
Rect(
|
||||
it.offset.toOffset().let {off ->
|
||||
if (isRtl) off.copy(x = state.gridState.layoutInfo.viewportSize.width - off.x - it.size.width)
|
||||
else off
|
||||
},
|
||||
it.offset.toOffset(),
|
||||
it.size.toSize()
|
||||
).contains(offset)
|
||||
}
|
||||
@@ -250,10 +243,7 @@ fun Modifier.dragAndDrop(
|
||||
val absPosition = state.draggedItemAbsolutePosition
|
||||
val draggedItem = state.draggedItem
|
||||
if (absPosition != null && draggedItem != null) {
|
||||
state.draggedItemAbsolutePosition = absPosition + dragAmount.let {
|
||||
if (isRtl) it.copy(x = -it.x)
|
||||
else it
|
||||
}
|
||||
state.draggedItemAbsolutePosition = absPosition + dragAmount
|
||||
val draggedCenter = Rect(absPosition, draggedItem.size.toSize()).center
|
||||
val dragOver = state.gridState.layoutInfo.visibleItemsInfo.find {
|
||||
Rect(
|
||||
@@ -308,7 +298,7 @@ fun LazyGridItemScope.DraggableItem(
|
||||
modifier = modifier
|
||||
.then(if (isDragged) Modifier else Modifier.animateItemPlacement())
|
||||
.zIndex(if (isDragged) 1f else 0f)
|
||||
.offset {
|
||||
.absoluteOffset {
|
||||
if (state.draggedItem?.key == key) {
|
||||
state.draggedItemOffset?.toIntOffset() ?: IntOffset.Zero
|
||||
} else if (state.droppedItemKey == key) {
|
||||
|
||||
@@ -12,17 +12,20 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import de.mm20.launcher2.globalactions.GlobalActionsService
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout
|
||||
import de.mm20.launcher2.preferences.BaseLayout
|
||||
import de.mm20.launcher2.preferences.ColorScheme
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.preferences.ScreenOrientation
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||
import de.mm20.launcher2.preferences.ui.GestureSettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -32,7 +35,8 @@ import org.koin.core.component.inject
|
||||
|
||||
class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
private val gestureSettings: GestureSettings by inject()
|
||||
private val globalActionsService: GlobalActionsService by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val searchableRepository: SavableSearchableRepository by inject()
|
||||
@@ -40,40 +44,41 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
private var isSystemInDarkMode = MutableStateFlow(false)
|
||||
|
||||
private val dimBackgroundState = combine(
|
||||
dataStore.data.map { it.appearance.dimWallpaper },
|
||||
dataStore.data.map { it.appearance.theme },
|
||||
uiSettings.dimWallpaper,
|
||||
uiSettings.colorScheme,
|
||||
isSystemInDarkMode
|
||||
) { dim, theme, systemDarkMode ->
|
||||
dim && (theme == Settings.AppearanceSettings.Theme.Dark || theme == Settings.AppearanceSettings.Theme.System && systemDarkMode)
|
||||
dim && (theme == ColorScheme.Dark || theme == ColorScheme.System && systemDarkMode)
|
||||
}
|
||||
val dimBackground = dimBackgroundState.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
val statusBarColor = dataStore.data.map { it.systemBars.statusBarColor }
|
||||
val statusBarColor = uiSettings.statusBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val navBarColor = dataStore.data.map { it.systemBars.statusBarColor }
|
||||
val navBarColor = uiSettings.navigationBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val chargingAnimation = dataStore.data.map { it.animations.charging }
|
||||
val chargingAnimation = uiSettings.chargingAnimation
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar }
|
||||
val hideNavBar = uiSettings.hideNavigationBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar }
|
||||
val hideStatusBar = uiSettings.hideStatusBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun setSystemInDarkMode(darkMode: Boolean) {
|
||||
isSystemInDarkMode.value = darkMode
|
||||
}
|
||||
|
||||
val baseLayout = dataStore.data.map { it.layout.baseLayout }
|
||||
val baseLayout = uiSettings.baseLayout
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }
|
||||
val bottomSearchBar = uiSettings.bottomSearchBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }
|
||||
val reverseSearchResults = uiSettings.reverseSearchResults
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar }
|
||||
val fixedSearchBar = uiSettings.fixedSearchBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val fixedRotation = dataStore.data.map { it.layout.fixedRotation }
|
||||
val fixedRotation = uiSettings.orientation
|
||||
.map { it != ScreenOrientation.Auto }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
val isSearchOpen = mutableStateOf(false)
|
||||
@@ -81,8 +86,7 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
|
||||
val searchBarFocused = mutableStateOf(false)
|
||||
|
||||
|
||||
val autoFocusSearch = dataStore.data.map { it.searchBar.autoFocus }
|
||||
val autoFocusSearch = uiSettings.openKeyboardOnSearch
|
||||
|
||||
fun setSearchbarFocus(focused: Boolean) {
|
||||
if (searchBarFocused.value != focused) searchBarFocused.value = focused
|
||||
@@ -120,45 +124,43 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
isWidgetEditMode.value = editMode
|
||||
}
|
||||
|
||||
val wallpaperBlur = dataStore.data.map { it.appearance.blurWallpaper }
|
||||
val wallpaperBlur = uiSettings.blurWallpaper
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
|
||||
val wallpaperBlurRadius = dataStore.data.map { it.appearance.blurWallpaperRadius }
|
||||
val wallpaperBlurRadius = uiSettings.wallpaperBlurRadius
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32)
|
||||
|
||||
|
||||
val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight }
|
||||
val fillClockHeight = uiSettings.clockFillScreen
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true)
|
||||
val searchBarColor = dataStore.data.map { it.searchBar.color }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarColors.Auto)
|
||||
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), Settings.SearchBarSettings.SearchBarStyle.Transparent)
|
||||
val searchBarColor = uiSettings.searchBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarColors.Auto)
|
||||
val searchBarStyle = uiSettings.searchBarStyle
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), SearchBarStyle.Transparent)
|
||||
|
||||
val gestureState: StateFlow<GestureState> = dataStore
|
||||
.data.map { it.gestures }
|
||||
.distinctUntilChanged()
|
||||
val gestureState: StateFlow<GestureState> = gestureSettings
|
||||
.combine(baseLayout) { settings, layout ->
|
||||
val swipeLeftAction =
|
||||
settings?.swipeLeft?.takeIf { layout != Layout.Pager } ?: GestureAction.None
|
||||
val swipeRightAction = settings?.swipeRight?.takeIf { layout != Layout.PagerReversed }
|
||||
?: GestureAction.None
|
||||
settings.swipeLeft.takeIf { layout != BaseLayout.Pager } ?: GestureAction.NoAction
|
||||
val swipeRightAction = settings.swipeRight.takeIf { layout != BaseLayout.PagerReversed }
|
||||
?: GestureAction.NoAction
|
||||
val swipeDownAction =
|
||||
settings?.swipeDown?.takeIf { layout != Layout.PullDown } ?: GestureAction.None
|
||||
val longPressAction = settings?.longPress ?: GestureAction.None
|
||||
val doubleTapAction = settings?.doubleTap ?: GestureAction.None
|
||||
val homeButtonAction = settings?.homeButton ?: GestureAction.None
|
||||
settings.swipeDown.takeIf { layout != BaseLayout.PullDown } ?: GestureAction.NoAction
|
||||
val longPressAction = settings.longPress
|
||||
val doubleTapAction = settings.doubleTap
|
||||
val homeButtonAction = settings.homeButton
|
||||
|
||||
val swipeLeftAppKey =
|
||||
if (swipeLeftAction == GestureAction.LaunchApp) settings.swipeLeftApp else null
|
||||
if (swipeLeftAction is GestureAction.Launch) swipeLeftAction.key else null
|
||||
val swipeRightAppKey =
|
||||
if (swipeRightAction == GestureAction.LaunchApp) settings.swipeRightApp else null
|
||||
if (swipeRightAction is GestureAction.Launch) swipeRightAction.key else null
|
||||
val swipeDownAppKey =
|
||||
if (swipeDownAction == GestureAction.LaunchApp) settings.swipeDownApp else null
|
||||
if (swipeDownAction is GestureAction.Launch) swipeDownAction.key else null
|
||||
val longPressAppKey =
|
||||
if (longPressAction == GestureAction.LaunchApp) settings.longPressApp else null
|
||||
if (longPressAction is GestureAction.Launch) longPressAction.key else null
|
||||
val doubleTapAppKey =
|
||||
if (doubleTapAction == GestureAction.LaunchApp) settings.doubleTapApp else null
|
||||
if (doubleTapAction is GestureAction.Launch) doubleTapAction.key else null
|
||||
val homeButtonAppKey =
|
||||
if (homeButtonAction == GestureAction.LaunchApp) settings.homeButtonApp else null
|
||||
if (homeButtonAction is GestureAction.Launch) homeButtonAction.key else null
|
||||
val apps = listOfNotNull(
|
||||
swipeLeftAppKey,
|
||||
swipeRightAppKey,
|
||||
@@ -189,17 +191,17 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
val action = when (gesture) {
|
||||
Gesture.DoubleTap -> gestureState.value.doubleTapAction
|
||||
Gesture.LongPress -> gestureState.value.longPressAction
|
||||
Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != Layout.PullDown }
|
||||
Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != Layout.Pager }
|
||||
Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != Layout.PagerReversed }
|
||||
Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != BaseLayout.PullDown }
|
||||
Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != BaseLayout.Pager }
|
||||
Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != BaseLayout.PagerReversed }
|
||||
Gesture.HomeButton -> gestureState.value.homeButtonAction
|
||||
}
|
||||
val requiresAccessibilityService =
|
||||
action == GestureAction.OpenRecents
|
||||
|| action == GestureAction.OpenPowerDialog
|
||||
|| action == GestureAction.OpenQuickSettings
|
||||
|| action == GestureAction.OpenNotificationDrawer
|
||||
|| action == GestureAction.LockScreen
|
||||
action is GestureAction.Recents
|
||||
|| action is GestureAction.PowerMenu
|
||||
|| action is GestureAction.QuickSettings
|
||||
|| action is GestureAction.Notifications
|
||||
|| action is GestureAction.ScreenLock
|
||||
|
||||
if (action != null && requiresAccessibilityService && !permissionsManager.checkPermissionOnce(
|
||||
PermissionGroup.Accessibility
|
||||
@@ -211,37 +213,37 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
|
||||
|
||||
return when (action) {
|
||||
GestureAction.OpenSearch -> {
|
||||
is GestureAction.Search -> {
|
||||
openSearch()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenNotificationDrawer -> {
|
||||
is GestureAction.Notifications -> {
|
||||
globalActionsService.openNotificationDrawer()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenQuickSettings -> {
|
||||
is GestureAction.QuickSettings -> {
|
||||
globalActionsService.openQuickSettings()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.LockScreen -> {
|
||||
is GestureAction.ScreenLock -> {
|
||||
globalActionsService.lockScreen()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenPowerDialog -> {
|
||||
is GestureAction.PowerMenu -> {
|
||||
globalActionsService.openPowerDialog()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenRecents -> {
|
||||
is GestureAction.Recents -> {
|
||||
globalActionsService.openRecents()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.LaunchApp -> {
|
||||
is GestureAction.Launch -> {
|
||||
val view = (context as Activity).window.decorView
|
||||
val options = ActivityOptionsCompat.makeScaleUpAnimation(
|
||||
view,
|
||||
@@ -271,12 +273,12 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
data class GestureState(
|
||||
val swipeLeftAction: GestureAction = GestureAction.None,
|
||||
val swipeRightAction: GestureAction = GestureAction.None,
|
||||
val swipeDownAction: GestureAction = GestureAction.None,
|
||||
val longPressAction: GestureAction = GestureAction.None,
|
||||
val doubleTapAction: GestureAction = GestureAction.None,
|
||||
val homeButtonAction: GestureAction = GestureAction.None,
|
||||
val swipeLeftAction: GestureAction = GestureAction.NoAction,
|
||||
val swipeRightAction: GestureAction = GestureAction.NoAction,
|
||||
val swipeDownAction: GestureAction = GestureAction.NoAction,
|
||||
val longPressAction: GestureAction = GestureAction.NoAction,
|
||||
val doubleTapAction: GestureAction = GestureAction.NoAction,
|
||||
val homeButtonAction: GestureAction = GestureAction.NoAction,
|
||||
val swipeLeftApp: SavableSearchable? = null,
|
||||
val swipeRightApp: SavableSearchable? = null,
|
||||
val swipeDownApp: SavableSearchable? = null,
|
||||
|
||||
@@ -81,11 +81,12 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.Velocity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||
@@ -398,7 +399,7 @@ fun PagerScaffold(
|
||||
.fillMaxSize()
|
||||
.nestedScroll(pagerNestedScrollConnection),
|
||||
beyondBoundsPageCount = 1,
|
||||
reverseLayout = reverse,
|
||||
reverseLayout = reverse == (LocalLayoutDirection.current == LayoutDirection.Ltr),
|
||||
state = pagerState,
|
||||
userScrollEnabled = false,//!isWidgetEditMode,
|
||||
flingBehavior = PagerDefaults.flingBehavior(
|
||||
@@ -406,7 +407,6 @@ fun PagerScaffold(
|
||||
lowVelocityAnimationSpec = spring(
|
||||
stiffness = Spring.StiffnessMediumLow,
|
||||
),
|
||||
snapVelocityThreshold = 1000.dp,
|
||||
pagerSnapDistance = remember {
|
||||
object : PagerSnapDistance {
|
||||
override fun calculateTargetPage(
|
||||
|
||||
@@ -68,7 +68,7 @@ import androidx.compose.ui.unit.Velocity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||
@@ -605,7 +605,7 @@ fun PullDownScaffold(
|
||||
showHiddenItemsButton = isSearchOpen,
|
||||
value = { value },
|
||||
onValueChange = { searchVM.search(it) },
|
||||
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == Settings.SearchBarSettings.SearchBarColors.Auto || searchBarColor == Settings.SearchBarSettings.SearchBarColors.Dark,
|
||||
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == SearchBarColors.Auto || searchBarColor == SearchBarColors.Dark,
|
||||
style = searchBarStyle,
|
||||
reverse = bottomSearchBar,
|
||||
onKeyboardActionGo = if (launchOnEnter) {
|
||||
|
||||
@@ -34,8 +34,8 @@ import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.SystemBarsSettings.SystemBarColors
|
||||
import de.mm20.launcher2.preferences.BaseLayout
|
||||
import de.mm20.launcher2.preferences.SystemBarColors
|
||||
import de.mm20.launcher2.ui.assistant.AssistantScaffold
|
||||
import de.mm20.launcher2.ui.base.BaseActivity
|
||||
import de.mm20.launcher2.ui.base.ProvideCurrentTime
|
||||
@@ -184,7 +184,7 @@ abstract class SharedLauncherActivity(
|
||||
}
|
||||
} else {
|
||||
when (layout) {
|
||||
Settings.LayoutSettings.Layout.PullDown -> {
|
||||
BaseLayout.PullDown -> {
|
||||
key(bottomSearchBar, reverseSearchResults) {
|
||||
PullDownScaffold(
|
||||
modifier = Modifier
|
||||
@@ -205,8 +205,8 @@ abstract class SharedLauncherActivity(
|
||||
}
|
||||
}
|
||||
|
||||
Settings.LayoutSettings.Layout.Pager,
|
||||
Settings.LayoutSettings.Layout.PagerReversed -> {
|
||||
BaseLayout.Pager,
|
||||
BaseLayout.PagerReversed -> {
|
||||
key(bottomSearchBar, reverseSearchResults) {
|
||||
PagerScaffold(
|
||||
modifier = Modifier
|
||||
@@ -220,7 +220,7 @@ abstract class SharedLauncherActivity(
|
||||
},
|
||||
darkStatusBarIcons = lightStatus,
|
||||
darkNavBarIcons = lightNav,
|
||||
reverse = layout == Settings.LayoutSettings.Layout.PagerReversed,
|
||||
reverse = layout == BaseLayout.PagerReversed,
|
||||
bottomSearchBar = bottomSearchBar,
|
||||
reverseSearchResults = reverseSearchResults,
|
||||
fixedSearchBar = fixedSearchBar,
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.ui.component.FakeSplashScreen
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
@@ -52,7 +52,7 @@ fun LauncherGestureHandler(
|
||||
|
||||
val gestureState by viewModel.gestureState.collectAsState(GestureState())
|
||||
|
||||
val shouldDetectDoubleTapGesture = gestureState.doubleTapAction != GestureAction.None
|
||||
val shouldDetectDoubleTapGesture = gestureState.doubleTapAction !is GestureAction.NoAction
|
||||
|
||||
LaunchedEffect(shouldDetectDoubleTapGesture) {
|
||||
gestureDetector.shouldDetectDoubleTaps = shouldDetectDoubleTapGesture
|
||||
|
||||
@@ -5,25 +5,30 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.files.settings.FileSearchSettings
|
||||
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.Ordering
|
||||
import de.mm20.launcher2.preferences.LegacySettings.SearchResultOrderingSettings.Ordering
|
||||
import de.mm20.launcher2.preferences.SearchResultOrder
|
||||
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.ContactSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.FavoritesSettings
|
||||
import de.mm20.launcher2.preferences.search.FileSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import de.mm20.launcher2.search.AppProfile
|
||||
import de.mm20.launcher2.search.AppShortcut
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.Article
|
||||
import de.mm20.launcher2.search.CalendarEvent
|
||||
import de.mm20.launcher2.search.Contact
|
||||
import de.mm20.launcher2.search.File
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.SearchService
|
||||
import de.mm20.launcher2.search.Searchable
|
||||
import de.mm20.launcher2.search.AppShortcut
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.search.Article
|
||||
import de.mm20.launcher2.search.CalendarEvent
|
||||
import de.mm20.launcher2.search.Website
|
||||
import de.mm20.launcher2.search.data.Calculator
|
||||
import de.mm20.launcher2.search.data.UnitConverter
|
||||
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.services.favorites.FavoritesService
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -32,7 +37,6 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
@@ -47,10 +51,14 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
private val favoritesService: FavoritesService by inject()
|
||||
private val searchableRepository: SavableSearchableRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val fileSearchSettings: FileSearchSettings by inject()
|
||||
|
||||
val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter }
|
||||
private val fileSearchSettings: FileSearchSettings by inject()
|
||||
private val contactSearchSettings: ContactSearchSettings by inject()
|
||||
private val calendarSearchSettings: CalendarSearchSettings by inject()
|
||||
private val shortcutSearchSettings: ShortcutSearchSettings by inject()
|
||||
private val searchUiSettings: SearchUiSettings by inject()
|
||||
|
||||
val launchOnEnter = searchUiSettings.launchOnEnter
|
||||
.stateIn(viewModelScope, SharingStarted.Eagerly, false)
|
||||
|
||||
private val searchService: SearchService by inject()
|
||||
@@ -70,10 +78,10 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
val unitConverterResults = mutableStateOf<List<UnitConverter>>(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 favoritesEnabled = dataStore.data.map { it.favorites.enabled }
|
||||
val favoritesEnabled = searchUiSettings.favorites
|
||||
val hideFavorites = mutableStateOf(false)
|
||||
|
||||
private val hiddenItemKeys = searchableRepository
|
||||
@@ -117,16 +125,9 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
hideFavorites.value = query.isNotEmpty()
|
||||
searchJob = viewModelScope.launch {
|
||||
dataStore.data.collectLatest { settings ->
|
||||
searchUiSettings.resultOrder.collectLatest { resultOrder ->
|
||||
searchService.search(
|
||||
query,
|
||||
calculator = settings.calculatorSearch,
|
||||
unitConverter = settings.unitConverterSearch,
|
||||
calendars = settings.calendarSearch,
|
||||
contacts = settings.contactsSearch,
|
||||
shortcuts = settings.appShortcutSearch,
|
||||
websites = settings.websiteSearch,
|
||||
wikipedia = settings.wikipediaSearch,
|
||||
query
|
||||
).collectLatest { results ->
|
||||
var resultsList = withContext(Dispatchers.Default) {
|
||||
listOfNotNull(
|
||||
@@ -151,13 +152,13 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
emptyList()
|
||||
} else {
|
||||
val keys = resultsList.mapNotNull { (it as? SavableSearchable)?.key }
|
||||
when (settings.resultOrdering.ordering) {
|
||||
when (resultOrder) {
|
||||
|
||||
Ordering.LaunchCount -> searchableRepository.sortByRelevance(
|
||||
SearchResultOrder.LaunchCount -> searchableRepository.sortByRelevance(
|
||||
keys
|
||||
).first()
|
||||
|
||||
Ordering.Weighted -> searchableRepository.sortByWeight(
|
||||
SearchResultOrder.Weighted -> searchableRepository.sortByWeight(
|
||||
keys
|
||||
).first()
|
||||
|
||||
@@ -256,7 +257,7 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
|
||||
val missingCalendarPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.Calendar),
|
||||
dataStore.data.map { it.calendarSearch.enabled }.distinctUntilChanged()
|
||||
calendarSearchSettings.enabled,
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestCalendarPermission(context: AppCompatActivity) {
|
||||
@@ -264,18 +265,12 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun disableCalendarSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCalendarSearch(it.calendarSearch.toBuilder().setEnabled(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
calendarSearchSettings.setEnabled(false)
|
||||
}
|
||||
|
||||
val missingContactsPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.Contacts),
|
||||
dataStore.data.map { it.contactsSearch.enabled }.distinctUntilChanged()
|
||||
contactSearchSettings.enabled
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestContactsPermission(context: AppCompatActivity) {
|
||||
@@ -283,18 +278,12 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun disableContactsSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setContactsSearch(it.contactsSearch.toBuilder().setEnabled(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
contactSearchSettings.setEnabled(false)
|
||||
}
|
||||
|
||||
val missingFilesPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.ExternalStorage),
|
||||
fileSearchSettings.localFiles.distinctUntilChanged()
|
||||
fileSearchSettings.localFiles
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestFilesPermission(context: AppCompatActivity) {
|
||||
@@ -302,18 +291,12 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun disableFilesSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFileSearch(it.fileSearch.toBuilder().setLocalFiles(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
fileSearchSettings.setLocalFiles(false)
|
||||
}
|
||||
|
||||
val missingAppShortcutPermission = combine(
|
||||
permissionsManager.hasPermission(PermissionGroup.AppShortcuts),
|
||||
dataStore.data.map { it.appShortcutSearch.enabled }.distinctUntilChanged()
|
||||
shortcutSearchSettings.enabled,
|
||||
) { perm, enabled -> !perm && enabled }
|
||||
|
||||
fun requestAppShortcutPermission(context: AppCompatActivity) {
|
||||
@@ -321,12 +304,6 @@ class SearchVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun disableAppShortcutSearch() {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppShortcutSearch(it.appShortcutSearch.toBuilder().setEnabled(false))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
shortcutSearchSettings.setEnabled(false)
|
||||
}
|
||||
}
|
||||
+6
-13
@@ -1,29 +1,22 @@
|
||||
package de.mm20.launcher2.ui.launcher.search.favorites
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.ui.UiState
|
||||
import de.mm20.launcher2.ui.common.FavoritesVM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class SearchFavoritesVM : FavoritesVM() {
|
||||
override val tagsExpanded: Flow<Boolean> = dataStore.data.map { it.ui.searchTagsMultiline }
|
||||
.shareIn(viewModelScope, SharingStarted.Lazily)
|
||||
private val uiState: UiState by inject()
|
||||
|
||||
override val tagsExpanded: Flow<Boolean> = uiState.favoritesTagsExpanded
|
||||
|
||||
override fun setTagsExpanded(expanded: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setUi(
|
||||
it.ui.toBuilder()
|
||||
.setSearchTagsMultiline(expanded)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiState.setFavoritesTagsExpanded(expanded)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||
import de.mm20.launcher2.searchactions.actions.SearchAction
|
||||
import de.mm20.launcher2.ui.component.SearchBar
|
||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||
@@ -29,7 +29,7 @@ import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
|
||||
@Composable
|
||||
fun LauncherSearchBar(
|
||||
modifier: Modifier = Modifier,
|
||||
style: Settings.SearchBarSettings.SearchBarStyle,
|
||||
style: SearchBarStyle,
|
||||
level: () -> SearchBarLevel,
|
||||
value: () -> String,
|
||||
onValueChange: (String) -> Unit,
|
||||
|
||||
@@ -9,6 +9,7 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
|
||||
import androidx.compose.animation.graphics.vector.AnimatedImageVector
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.HelpOutline
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material.icons.rounded.Wallpaper
|
||||
@@ -20,6 +21,7 @@ import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -27,7 +29,10 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM
|
||||
import de.mm20.launcher2.ui.launcher.widgets.WidgetsVM
|
||||
import de.mm20.launcher2.ui.settings.SettingsActivity
|
||||
|
||||
@Composable
|
||||
@@ -38,6 +43,8 @@ fun RowScope.SearchBarMenu(
|
||||
val context = LocalContext.current
|
||||
var showOverflowMenu by remember { mutableStateOf(false) }
|
||||
val rightIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_menu_clear)
|
||||
val launcherVM: LauncherScaffoldVM = viewModel()
|
||||
val widgetsVM: WidgetsVM = viewModel()
|
||||
|
||||
IconButton(onClick = {
|
||||
if (searchBarValue.isNotBlank()) onSearchBarValueChange("")
|
||||
@@ -70,6 +77,22 @@ fun RowScope.SearchBarMenu(
|
||||
Icon(imageVector = Icons.Rounded.Wallpaper, contentDescription = null)
|
||||
}
|
||||
)
|
||||
val editButton by widgetsVM.editButton.collectAsState()
|
||||
val searchOpen by launcherVM.isSearchOpen
|
||||
if (!searchOpen && editButton == false) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
launcherVM.setWidgetEditMode(editMode = true)
|
||||
showOverflowMenu = false
|
||||
},
|
||||
text = {
|
||||
Text(stringResource(R.string.menu_edit_widgets))
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(imageVector = Icons.Rounded.Edit, contentDescription = null)
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
context.startActivity(Intent(context, SettingsActivity::class.java))
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package de.mm20.launcher2.ui.launcher.sheets
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.LauncherApps
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.IntentSenderRequest
|
||||
@@ -33,7 +31,6 @@ import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Settings
|
||||
import androidx.compose.material.icons.rounded.Tag
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.FilledTonalIconButton
|
||||
@@ -65,12 +62,13 @@ import androidx.compose.ui.graphics.drawOutline
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import androidx.compose.ui.unit.toSize
|
||||
@@ -173,6 +171,7 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: Padding
|
||||
val tagsListState = rememberLazyListState()
|
||||
val tagsTitleSize = 48.dp.toPixels()
|
||||
val tagsSpacing = 12.dp.toPixels()
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val state = rememberLazyDragAndDropGridState(
|
||||
gridState = gridState,
|
||||
onDragStart = {
|
||||
@@ -200,8 +199,11 @@ fun ReorderFavoritesGrid(viewModel: EditFavoritesSheetVM, paddingValues: Padding
|
||||
&& hoveredItem.offset.y + tagsTitleSize < position.y
|
||||
) {
|
||||
val scroll = tagsListState.layoutInfo.viewportStartOffset
|
||||
val relCenter =
|
||||
if (isRtl) draggedCenter.copy(x = tagsListState.layoutInfo.viewportSize.width - draggedCenter.x)
|
||||
else draggedCenter
|
||||
val tag = tagsListState.layoutInfo.visibleItemsInfo.find {
|
||||
draggedCenter.x + scroll > it.offset && draggedCenter.x + scroll < it.offset + it.size - tagsSpacing
|
||||
relCenter.x + scroll > it.offset && relCenter.x + scroll < it.offset + it.size - tagsSpacing
|
||||
}
|
||||
hoveredTag = tag?.index?.let { pinnedTags[it].tag }
|
||||
} else {
|
||||
|
||||
@@ -17,9 +17,9 @@ import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.ktx.normalize
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.appshortcuts.AppShortcut
|
||||
import de.mm20.launcher2.preferences.search.FavoritesSettings
|
||||
import de.mm20.launcher2.search.Searchable
|
||||
import de.mm20.launcher2.search.data.Tag
|
||||
import de.mm20.launcher2.services.favorites.FavoritesService
|
||||
@@ -41,7 +41,7 @@ class EditFavoritesSheetVM : ViewModel(), KoinComponent {
|
||||
private val badgeService: BadgeService by inject()
|
||||
private val customAttributesRepository: CustomAttributesRepository by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val favoritesSettings: FavoritesSettings by inject()
|
||||
|
||||
val gridItems = mutableStateOf<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)
|
||||
fun setFrequentlyUsed(frequentlyUsed: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFavorites(
|
||||
it.favorites
|
||||
.toBuilder()
|
||||
.setFrequentlyUsed(frequentlyUsed)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
favoritesSettings.setFrequentlyUsed(frequentlyUsed)
|
||||
}
|
||||
|
||||
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
|
||||
val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFavorites(
|
||||
it.favorites
|
||||
.toBuilder()
|
||||
.setFrequentlyUsedRows(frequentlyUsedRows)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows)
|
||||
}
|
||||
|
||||
fun pinTag(tag: Tag) {
|
||||
|
||||
@@ -14,7 +14,8 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
@@ -29,12 +30,12 @@ fun FailedGestureSheet(
|
||||
val viewModel: FailedGestureSheetVM = viewModel()
|
||||
|
||||
val actionName = stringResource(when(failedGesture.action) {
|
||||
Settings.GestureSettings.GestureAction.OpenSearch -> R.string.gesture_action_open_search
|
||||
Settings.GestureSettings.GestureAction.OpenNotificationDrawer -> R.string.gesture_action_notifications
|
||||
Settings.GestureSettings.GestureAction.LockScreen -> R.string.gesture_action_lock_screen
|
||||
Settings.GestureSettings.GestureAction.OpenQuickSettings -> R.string.gesture_action_quick_settings
|
||||
Settings.GestureSettings.GestureAction.OpenRecents -> R.string.gesture_action_recents
|
||||
Settings.GestureSettings.GestureAction.OpenPowerDialog -> R.string.gesture_action_power_menu
|
||||
is GestureAction.Search -> R.string.gesture_action_open_search
|
||||
is GestureAction.Notifications -> R.string.gesture_action_notifications
|
||||
is GestureAction.ScreenLock -> R.string.gesture_action_lock_screen
|
||||
is GestureAction.QuickSettings -> R.string.gesture_action_quick_settings
|
||||
is GestureAction.Recents -> R.string.gesture_action_recents
|
||||
is GestureAction.PowerMenu -> R.string.gesture_action_power_menu
|
||||
else -> R.string.gesture_action_none
|
||||
})
|
||||
val gestureName = stringResource(when(failedGesture.gesture) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.preferences.ui.GestureSettings
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
@@ -14,28 +14,20 @@ import org.koin.core.component.inject
|
||||
|
||||
class FailedGestureSheetVM : ViewModel(), KoinComponent {
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val gestureSettings: GestureSettings by inject()
|
||||
|
||||
fun requestPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
|
||||
}
|
||||
|
||||
fun disableGesture(gesture: Gesture) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(
|
||||
it.gestures.toBuilder().apply {
|
||||
when (gesture) {
|
||||
Gesture.SwipeDown -> swipeDown = GestureAction.None
|
||||
Gesture.SwipeLeft -> swipeLeft = GestureAction.None
|
||||
Gesture.SwipeRight -> swipeRight = GestureAction.None
|
||||
Gesture.DoubleTap -> doubleTap = GestureAction.None
|
||||
Gesture.LongPress -> longPress = GestureAction.None
|
||||
Gesture.HomeButton -> homeButton = GestureAction.None
|
||||
}
|
||||
}.build()
|
||||
).build()
|
||||
}
|
||||
when(gesture) {
|
||||
Gesture.DoubleTap -> gestureSettings.setDoubleTap(GestureAction.NoAction)
|
||||
Gesture.LongPress -> gestureSettings.setLongPress(GestureAction.NoAction)
|
||||
Gesture.SwipeDown -> gestureSettings.setSwipeDown(GestureAction.NoAction)
|
||||
Gesture.SwipeLeft -> gestureSettings.setSwipeLeft(GestureAction.NoAction)
|
||||
Gesture.SwipeRight -> gestureSettings.setSwipeRight(GestureAction.NoAction)
|
||||
Gesture.HomeButton -> gestureSettings.setHomeButton(GestureAction.NoAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ fun WidgetColumn(
|
||||
}
|
||||
|
||||
val editButton by viewModel.editButton.collectAsState()
|
||||
if (editButton == true) {
|
||||
if (editMode || editButton == true) {
|
||||
val icon =
|
||||
AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_edit_add)
|
||||
ExtendedFloatingActionButton(
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.widgets.Widget
|
||||
import de.mm20.launcher2.widgets.WidgetRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -15,9 +14,9 @@ import org.koin.core.component.inject
|
||||
class WidgetsVM : ViewModel(), KoinComponent {
|
||||
private val widgetRepository: WidgetRepository by inject()
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val editButton = dataStore.data.map { it.widgets.editButton }
|
||||
val editButton = uiSettings.widgetEditButton
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val widgets = widgetRepository.get()
|
||||
|
||||
@@ -14,8 +14,6 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -64,10 +62,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetAlignment
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import de.mm20.launcher2.preferences.ClockWidgetAlignment
|
||||
import de.mm20.launcher2.preferences.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.base.LocalTime
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
@@ -89,7 +86,7 @@ fun ClockWidget(
|
||||
) {
|
||||
val viewModel: ClockWidgetVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val layout by viewModel.layout.collectAsState()
|
||||
val compact by viewModel.compactLayout.collectAsState()
|
||||
val clockStyle by viewModel.clockStyle.collectAsState()
|
||||
val color by viewModel.color.collectAsState()
|
||||
val alignment by viewModel.alignment.collectAsState()
|
||||
@@ -106,7 +103,9 @@ fun ClockWidget(
|
||||
viewModel.updateTime(time)
|
||||
}
|
||||
|
||||
val partProvider by remember { viewModel.getActivePart(context) }.collectAsStateWithLifecycle(null)
|
||||
val partProvider by remember { viewModel.getActivePart(context) }.collectAsStateWithLifecycle(
|
||||
null
|
||||
)
|
||||
|
||||
AnimatedContent(editMode, label = "ClockWidget") {
|
||||
if (it) {
|
||||
@@ -165,32 +164,32 @@ fun ClockWidget(
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides contentColor
|
||||
) {
|
||||
if (layout == ClockWidgetLayout.Vertical) {
|
||||
if (compact == false) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.clickable(
|
||||
enabled = clockStyle != ClockStyle.EmptyClock,
|
||||
enabled = clockStyle !is ClockWidgetStyle.Empty,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
viewModel.launchClockApp(context)
|
||||
}
|
||||
) {
|
||||
Clock(clockStyle, ClockWidgetLayout.Vertical)
|
||||
Clock(clockStyle, false)
|
||||
}
|
||||
|
||||
if (partProvider != null) {
|
||||
DynamicZone(
|
||||
modifier = Modifier.padding(bottom = 8.dp),
|
||||
layout = ClockWidgetLayout.Vertical,
|
||||
compact = false,
|
||||
provider = partProvider,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout == ClockWidgetLayout.Horizontal) {
|
||||
if (compact == true) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -201,7 +200,7 @@ fun ClockWidget(
|
||||
if (partProvider != null) {
|
||||
DynamicZone(
|
||||
modifier = Modifier.weight(1f),
|
||||
layout = ClockWidgetLayout.Horizontal,
|
||||
compact = true,
|
||||
provider = partProvider,
|
||||
)
|
||||
}
|
||||
@@ -216,14 +215,14 @@ fun ClockWidget(
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier.clickable(
|
||||
enabled = clockStyle != ClockStyle.EmptyClock,
|
||||
enabled = clockStyle !is ClockWidgetStyle.Empty,
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
viewModel.launchClockApp(context)
|
||||
}
|
||||
) {
|
||||
Clock(clockStyle, ClockWidgetLayout.Horizontal)
|
||||
Clock(clockStyle, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -236,7 +235,7 @@ fun ClockWidget(
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
dockProvider?.Component(ClockWidgetLayout.Vertical)
|
||||
dockProvider?.Component(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,21 +245,18 @@ fun ClockWidget(
|
||||
|
||||
@Composable
|
||||
fun Clock(
|
||||
style: ClockStyle?,
|
||||
layout: ClockWidgetLayout
|
||||
style: ClockWidgetStyle?,
|
||||
compact: Boolean,
|
||||
) {
|
||||
val time = LocalTime.current
|
||||
when (style) {
|
||||
ClockStyle.DigitalClock1,
|
||||
ClockStyle.DigitalClock1_Outlined,
|
||||
ClockStyle.DigitalClock1_MDY,
|
||||
ClockStyle.DigitalClock1_OnePlus -> DigitalClock1(time, layout, style)
|
||||
is ClockWidgetStyle.Digital1 -> DigitalClock1(time, compact, style)
|
||||
|
||||
ClockStyle.DigitalClock2 -> DigitalClock2(time, layout)
|
||||
ClockStyle.BinaryClock -> BinaryClock(time, layout)
|
||||
ClockStyle.AnalogClock -> AnalogClock(time, layout)
|
||||
ClockStyle.OrbitClock -> OrbitClock(time, layout)
|
||||
ClockStyle.EmptyClock -> {}
|
||||
is ClockWidgetStyle.Digital2 -> DigitalClock2(time, compact)
|
||||
is ClockWidgetStyle.Binary -> BinaryClock(time, compact)
|
||||
is ClockWidgetStyle.Analog -> AnalogClock(time, compact)
|
||||
is ClockWidgetStyle.Orbit -> OrbitClock(time, compact)
|
||||
is ClockWidgetStyle.Empty -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -268,13 +264,13 @@ fun Clock(
|
||||
@Composable
|
||||
fun DynamicZone(
|
||||
modifier: Modifier = Modifier,
|
||||
layout: ClockWidgetLayout,
|
||||
compact: Boolean,
|
||||
provider: PartProvider?,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
provider?.Component(layout)
|
||||
provider?.Component(compact)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,18 +279,13 @@ fun ConfigureClockWidgetSheet(
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val viewModel: ClockWidgetSettingsScreenVM = viewModel()
|
||||
val layout by viewModel.layout.collectAsState()
|
||||
val compact by viewModel.compact.collectAsState()
|
||||
val color by viewModel.color.collectAsState()
|
||||
val style by viewModel.clockStyle.collectAsState()
|
||||
val fillHeight by viewModel.fillHeight.collectAsState()
|
||||
val alignment by viewModel.alignment.collectAsState()
|
||||
|
||||
val date by viewModel.datePart.collectAsState()
|
||||
val favorites by viewModel.favoritesPart.collectAsState()
|
||||
val media by viewModel.musicPart.collectAsState()
|
||||
val alarm by viewModel.alarmPart.collectAsState()
|
||||
val battery by viewModel.batteryPart.collectAsState()
|
||||
|
||||
val dock by viewModel.dock.collectAsState()
|
||||
val parts by viewModel.parts.collectAsState()
|
||||
|
||||
BottomSheetDialog(onDismissRequest = onDismiss) {
|
||||
Column(
|
||||
@@ -307,14 +298,14 @@ fun ConfigureClockWidgetSheet(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
SegmentedButton(
|
||||
selected = layout == ClockWidgetLayout.Vertical,
|
||||
selected = compact == false,
|
||||
onClick = {
|
||||
viewModel.setLayout(ClockWidgetLayout.Vertical)
|
||||
viewModel.setCompact(false)
|
||||
},
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 0, count = 2),
|
||||
icon = {
|
||||
SegmentedButtonDefaults.Icon(
|
||||
active = layout == ClockWidgetLayout.Vertical,
|
||||
active = compact == false,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.HorizontalSplit,
|
||||
@@ -327,14 +318,14 @@ fun ConfigureClockWidgetSheet(
|
||||
Text(text = stringResource(R.string.preference_clockwidget_layout_vertical))
|
||||
}
|
||||
SegmentedButton(
|
||||
selected = layout == ClockWidgetLayout.Horizontal,
|
||||
selected = compact == true,
|
||||
onClick = {
|
||||
viewModel.setLayout(ClockWidgetLayout.Horizontal)
|
||||
viewModel.setCompact(true)
|
||||
},
|
||||
shape = SegmentedButtonDefaults.itemShape(index = 1, count = 2),
|
||||
icon = {
|
||||
SegmentedButtonDefaults.Icon(
|
||||
active = layout == ClockWidgetLayout.Horizontal,
|
||||
active = compact == true,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.VerticalSplit,
|
||||
@@ -348,9 +339,9 @@ fun ConfigureClockWidgetSheet(
|
||||
}
|
||||
}
|
||||
|
||||
if (color != null && layout != null) {
|
||||
if (color != null && compact != null) {
|
||||
WatchFaceSelector(
|
||||
layout = layout!!,
|
||||
compact = compact!!,
|
||||
colors = color!!,
|
||||
selected = style,
|
||||
onSelect = {
|
||||
@@ -487,7 +478,7 @@ fun ConfigureClockWidgetSheet(
|
||||
title = stringResource(R.string.preference_clockwidget_favorites_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_favorites_part_summary),
|
||||
icon = Icons.Rounded.Star,
|
||||
value = favorites == true,
|
||||
value = dock == true,
|
||||
onValueChanged = {
|
||||
viewModel.setFavoritesPart(it)
|
||||
}
|
||||
@@ -510,7 +501,7 @@ fun ConfigureClockWidgetSheet(
|
||||
title = stringResource(R.string.preference_clockwidget_date_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_date_part_summary),
|
||||
icon = Icons.Rounded.Today,
|
||||
value = date == true,
|
||||
value = parts?.date == true,
|
||||
onValueChanged = {
|
||||
viewModel.setDatePart(it)
|
||||
}
|
||||
@@ -519,7 +510,7 @@ fun ConfigureClockWidgetSheet(
|
||||
title = stringResource(R.string.preference_clockwidget_music_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_music_part_summary),
|
||||
icon = Icons.Rounded.MusicNote,
|
||||
value = media == true,
|
||||
value = parts?.music == true,
|
||||
onValueChanged = {
|
||||
viewModel.setMusicPart(it)
|
||||
}
|
||||
@@ -528,7 +519,7 @@ fun ConfigureClockWidgetSheet(
|
||||
title = stringResource(R.string.preference_clockwidget_alarm_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_alarm_part_summary),
|
||||
icon = Icons.Rounded.Alarm,
|
||||
value = alarm == true,
|
||||
value = parts?.alarm == true,
|
||||
onValueChanged = {
|
||||
viewModel.setAlarmPart(it)
|
||||
}
|
||||
@@ -537,7 +528,7 @@ fun ConfigureClockWidgetSheet(
|
||||
title = stringResource(R.string.preference_clockwidget_battery_part),
|
||||
summary = stringResource(R.string.preference_clockwidget_battery_part_summary),
|
||||
icon = Icons.Rounded.BatteryFull,
|
||||
value = battery == true,
|
||||
value = parts?.battery == true,
|
||||
onValueChanged = {
|
||||
viewModel.setBatteryPart(it)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.provider.AlarmClock
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.AlarmPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.BatteryPartProvider
|
||||
import de.mm20.launcher2.ui.launcher.widgets.clock.parts.DatePartProvider
|
||||
@@ -18,21 +18,20 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val settings: ClockWidgetSettings by inject()
|
||||
|
||||
private val partProviders = dataStore.data.map { it.clockWidget }.distinctUntilChanged().map {
|
||||
private val partProviders = settings.parts.map {
|
||||
val providers = mutableListOf<PartProvider>()
|
||||
if (it.datePart) providers += DatePartProvider()
|
||||
if (it.musicPart) providers += MusicPartProvider()
|
||||
if (it.batteryPart) providers += BatteryPartProvider()
|
||||
if (it.alarmPart) providers += AlarmPartProvider()
|
||||
if (it.date) providers += DatePartProvider()
|
||||
if (it.music) providers += MusicPartProvider()
|
||||
if (it.battery) providers += BatteryPartProvider()
|
||||
if (it.alarm) providers += AlarmPartProvider()
|
||||
providers
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
@@ -51,19 +50,19 @@ class ClockWidgetVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
}
|
||||
|
||||
val layout = dataStore.data.map { it.clockWidget.layout }
|
||||
val compactLayout = settings.compact
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }
|
||||
val clockStyle = settings.clockStyle
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val color = dataStore.data.map { it.clockWidget.color }
|
||||
val color = settings.color
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val alignment = dataStore.data.map { it.clockWidget.alignment }
|
||||
val alignment = settings.alignment
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val dockProvider = dataStore.data
|
||||
.map { if (it.clockWidget.favoritesPart) FavoritesPartProvider() else null }
|
||||
val dockProvider = settings.dock
|
||||
.map { if (it) FavoritesPartProvider() else null }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun updateTime(time: Long) {
|
||||
|
||||
+25
-13
@@ -43,19 +43,18 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import de.mm20.launcher2.preferences.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.locals.LocalPreferDarkContentOverWallpaper
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun WatchFaceSelector(
|
||||
layout: ClockWidgetLayout,
|
||||
compact: Boolean,
|
||||
colors: ClockWidgetColors,
|
||||
selected: ClockStyle?,
|
||||
onSelect: (ClockStyle) -> Unit,
|
||||
selected: ClockWidgetStyle?,
|
||||
onSelect: (ClockWidgetStyle) -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
Surface(
|
||||
@@ -73,7 +72,7 @@ fun WatchFaceSelector(
|
||||
modifier = Modifier,
|
||||
) {
|
||||
val styles = remember {
|
||||
sortedMapOf(
|
||||
mapOf(
|
||||
ClockStyle.DigitalClock1 to 0,
|
||||
ClockStyle.DigitalClock1_Outlined to 0,
|
||||
ClockStyle.DigitalClock1_MDY to 0,
|
||||
@@ -159,9 +158,9 @@ fun WatchFaceSelector(
|
||||
styles.filter { it.value == pageIndex }
|
||||
}
|
||||
if (currentPageStyles.containsKey(selected)) {
|
||||
Clock(selected, layout)
|
||||
Clock(selected, compact)
|
||||
} else {
|
||||
Clock(currentPageStyles.keys.first(), layout)
|
||||
Clock(currentPageStyles.keys.first(), compact)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,7 +252,7 @@ fun WatchFaceSelector(
|
||||
}
|
||||
}
|
||||
|
||||
fun getClockstyleName(context: Context, style: ClockStyle): String {
|
||||
fun getClockstyleName(context: Context, style: ClockWidgetStyle): String {
|
||||
return when (style) {
|
||||
ClockStyle.DigitalClock1,
|
||||
ClockStyle.DigitalClock1_Outlined,
|
||||
@@ -264,11 +263,11 @@ fun getClockstyleName(context: Context, style: ClockStyle): String {
|
||||
ClockStyle.BinaryClock -> "Binary"
|
||||
ClockStyle.AnalogClock -> "Hands"
|
||||
ClockStyle.EmptyClock -> "Empty"
|
||||
ClockStyle.UNRECOGNIZED -> ""
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun getVariantName(context: Context, style: ClockStyle): String {
|
||||
fun getVariantName(context: Context, style: ClockWidgetStyle): String {
|
||||
return when (style) {
|
||||
ClockStyle.DigitalClock1,
|
||||
ClockStyle.DigitalClock2,
|
||||
@@ -279,7 +278,20 @@ fun getVariantName(context: Context, style: ClockStyle): String {
|
||||
ClockStyle.DigitalClock1_Outlined -> "Outlined"
|
||||
ClockStyle.DigitalClock1_MDY -> "Material You"
|
||||
ClockStyle.DigitalClock1_OnePlus -> "OnePlus"
|
||||
else -> ""
|
||||
|
||||
ClockStyle.UNRECOGNIZED -> ""
|
||||
}
|
||||
}
|
||||
|
||||
// Compat for old enum names, TODO refactor this screen
|
||||
object ClockStyle {
|
||||
val DigitalClock1 = ClockWidgetStyle.Digital1()
|
||||
val DigitalClock1_Outlined = ClockWidgetStyle.Digital1(outlined = true)
|
||||
val DigitalClock1_MDY = ClockWidgetStyle.Digital1(variant = ClockWidgetStyle.Digital1.Variant.MDY)
|
||||
val DigitalClock1_OnePlus = ClockWidgetStyle.Digital1(variant = ClockWidgetStyle.Digital1.Variant.OnePlus)
|
||||
val DigitalClock2 = ClockWidgetStyle.Digital2
|
||||
val OrbitClock = ClockWidgetStyle.Orbit
|
||||
val AnalogClock = ClockWidgetStyle.Analog
|
||||
val BinaryClock = ClockWidgetStyle.Binary
|
||||
val EmptyClock = ClockWidgetStyle.Empty
|
||||
}
|
||||
+3
-3
@@ -11,15 +11,15 @@ import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.rotate
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import java.util.*
|
||||
|
||||
@Composable
|
||||
fun AnalogClock(
|
||||
time: Long,
|
||||
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
compact: Boolean,
|
||||
) {
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val verticalLayout = !compact
|
||||
val date = Calendar.getInstance()
|
||||
date.timeInMillis = time
|
||||
val minute = date[Calendar.MINUTE]
|
||||
|
||||
+2
-3
@@ -7,15 +7,14 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import java.util.*
|
||||
|
||||
@Composable
|
||||
fun BinaryClock(
|
||||
time: Long,
|
||||
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
compact: Boolean,
|
||||
) {
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val verticalLayout = !compact
|
||||
val date = Calendar.getInstance()
|
||||
date.timeInMillis = time
|
||||
val minute = date[Calendar.MINUTE]
|
||||
|
||||
+7
-9
@@ -8,7 +8,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.drawscope.DrawStyle
|
||||
import androidx.compose.ui.graphics.drawscope.Fill
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -20,8 +19,7 @@ import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import androidx.compose.ui.unit.sp
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockStyle
|
||||
import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -30,10 +28,10 @@ import java.util.*
|
||||
@Composable
|
||||
fun DigitalClock1(
|
||||
time: Long,
|
||||
layout: Settings.ClockWidgetSettings.ClockWidgetLayout,
|
||||
variant: ClockStyle = ClockStyle.DigitalClock1,
|
||||
compact: Boolean,
|
||||
style: ClockWidgetStyle.Digital1 = ClockWidgetStyle.Digital1(),
|
||||
) {
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val verticalLayout = !compact
|
||||
val format = SimpleDateFormat(
|
||||
if (verticalLayout) {
|
||||
if (DateFormat.is24HourFormat(LocalContext.current)) "HH\nmm" else "hh\nmm"
|
||||
@@ -50,8 +48,8 @@ fun DigitalClock1(
|
||||
fontWeight = FontWeight.Black,
|
||||
textAlign = TextAlign.Center,
|
||||
lineHeight = 0.8.em,
|
||||
drawStyle = if (variant == ClockStyle.DigitalClock1_Outlined) Stroke(width = 2.dp.toPixels()) else Fill,
|
||||
color = if (variant == ClockStyle.DigitalClock1_MDY) {
|
||||
drawStyle = if (style.outlined) Stroke(width = 2.dp.toPixels()) else Fill,
|
||||
color = if (style.variant == ClockWidgetStyle.Digital1.Variant.MDY) {
|
||||
if (LocalContentColor.current == Color.White) {
|
||||
if (LocalDarkTheme.current) MaterialTheme.colorScheme.onPrimaryContainer
|
||||
else MaterialTheme.colorScheme.primaryContainer
|
||||
@@ -64,7 +62,7 @@ fun DigitalClock1(
|
||||
|
||||
val modifier = Modifier.offset(0.dp, if (verticalLayout) 16.dp else 0.dp)
|
||||
|
||||
if (variant == ClockStyle.DigitalClock1_OnePlus) {
|
||||
if (style.variant == ClockWidgetStyle.Digital1.Variant.OnePlus) {
|
||||
val hour = formattedString.substring(0, 2)
|
||||
Text(
|
||||
modifier = modifier,
|
||||
|
||||
+1
-2
@@ -6,13 +6,12 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
|
||||
|
||||
@Composable
|
||||
fun DigitalClock2(
|
||||
time: Long,
|
||||
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
compact: Boolean,
|
||||
) {
|
||||
Text(
|
||||
text = DateUtils.formatDateTime(LocalContext.current, time, DateUtils.FORMAT_SHOW_TIME),
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.center
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.toOffset
|
||||
import de.mm20.launcher2.ktx.TWO_PI
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
@@ -46,9 +46,9 @@ private val currentTime
|
||||
@Composable
|
||||
fun OrbitClock(
|
||||
_time: Long,
|
||||
layout: Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
compact: Boolean,
|
||||
) {
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val verticalLayout = !compact
|
||||
|
||||
val timeState = remember { mutableStateOf<ZonedDateTime>(currentTime) }
|
||||
|
||||
|
||||
+2
-3
@@ -21,7 +21,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
@@ -71,7 +70,7 @@ class AlarmPartProvider : PartProvider {
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: ClockWidgetLayout) {
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val alarmTime by nextAlarmTime
|
||||
@@ -79,7 +78,7 @@ class AlarmPartProvider : PartProvider {
|
||||
|
||||
alarmTime?.let {
|
||||
|
||||
if (layout == ClockWidgetLayout.Vertical) {
|
||||
if (!compactLayout) {
|
||||
|
||||
TextButton(
|
||||
onClick = {
|
||||
|
||||
+3
-5
@@ -20,14 +20,12 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.icons.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -54,13 +52,13 @@ class BatteryPartProvider : PartProvider {
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) {
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
|
||||
val batteryInfo by this.batteryInfo.collectAsState(null)
|
||||
|
||||
batteryInfo?.let {
|
||||
|
||||
if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical) {
|
||||
if (!compactLayout) {
|
||||
Row(
|
||||
Modifier.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -86,7 +84,7 @@ class BatteryPartProvider : PartProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Horizontal) {
|
||||
if (compactLayout) {
|
||||
Row(
|
||||
Modifier.padding(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
||||
+2
-3
@@ -12,7 +12,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.em
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.ui.base.LocalTime
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@@ -25,9 +24,9 @@ class DatePartProvider : PartProvider {
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: Settings.ClockWidgetSettings.ClockWidgetLayout) {
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
val time = LocalTime.current
|
||||
val verticalLayout = layout == Settings.ClockWidgetSettings.ClockWidgetLayout.Vertical
|
||||
val verticalLayout = !compactLayout
|
||||
val context = LocalContext.current
|
||||
TextButton(
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
|
||||
+7
-11
@@ -3,16 +3,13 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
import android.content.Context
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.services.favorites.FavoritesService
|
||||
import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid
|
||||
import de.mm20.launcher2.widgets.CalendarWidget
|
||||
@@ -27,25 +24,24 @@ class FavoritesPartProvider : PartProvider, KoinComponent {
|
||||
|
||||
private val favoritesService: FavoritesService by inject()
|
||||
private val widgetRepository: WidgetRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
override fun getRanking(context: Context): Flow<Int> = flow {
|
||||
emit(Int.MAX_VALUE)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: ClockWidgetLayout) {
|
||||
val columns by remember(layout) {
|
||||
dataStore.data.map {
|
||||
val c = it.grid.columnCount
|
||||
if (layout == ClockWidgetLayout.Horizontal) c - 2 else c
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
val columns by remember {
|
||||
uiSettings.gridSettings.map {
|
||||
it.columnCount
|
||||
}
|
||||
}.collectAsState(0)
|
||||
val excludeCalendar by remember { widgetRepository.exists(CalendarWidget.Type) }.collectAsState(
|
||||
true
|
||||
)
|
||||
|
||||
val favorites by remember(columns, excludeCalendar, layout) {
|
||||
val favorites by remember(columns, excludeCalendar) {
|
||||
favoritesService.getFavorites(
|
||||
excludeTypes = if (excludeCalendar) listOf("calendar", "tag") else listOf("tag"),
|
||||
manuallySorted = true,
|
||||
|
||||
+3
-4
@@ -28,7 +28,6 @@ import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.music.MusicService
|
||||
import de.mm20.launcher2.music.PlaybackState
|
||||
import de.mm20.launcher2.music.SupportedActions
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
@@ -48,7 +47,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Component(layout: ClockWidgetLayout) {
|
||||
override fun Component(compactLayout: Boolean) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val title by musicService.title.collectAsState(null)
|
||||
@@ -58,7 +57,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
|
||||
|
||||
val playIcon = AnimatedImageVector.animatedVectorResource(R.drawable.anim_ic_play_pause)
|
||||
|
||||
if (layout === ClockWidgetLayout.Horizontal) {
|
||||
if (compactLayout) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -113,7 +112,7 @@ class MusicPartProvider : PartProvider, KoinComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layout === ClockWidgetLayout.Vertical) {
|
||||
if (!compactLayout) {
|
||||
Column(
|
||||
modifier = Modifier.padding(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.launcher.widgets.clock.parts
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface PartProvider {
|
||||
@@ -12,5 +11,5 @@ interface PartProvider {
|
||||
fun setTime(time: Long) {}
|
||||
|
||||
@Composable
|
||||
fun Component(layout: ClockWidgetLayout)
|
||||
fun Component(compactLayout: Boolean)
|
||||
}
|
||||
+5
@@ -7,6 +7,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.Tag
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -30,6 +31,10 @@ fun FavoritesWidget(widget: FavoritesWidget) {
|
||||
|
||||
val tagsExpanded by viewModel.tagsExpanded.collectAsState(false)
|
||||
|
||||
LaunchedEffect(widget) {
|
||||
viewModel.updateWidget(widget)
|
||||
}
|
||||
|
||||
Column {
|
||||
if (favorites.isNotEmpty()) {
|
||||
SearchResultGrid(favorites)
|
||||
|
||||
+28
-26
@@ -1,32 +1,34 @@
|
||||
package de.mm20.launcher2.ui.launcher.widgets.favorites
|
||||
|
||||
import de.mm20.launcher2.preferences.ui.GridSettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.services.widgets.WidgetsService
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetLayout
|
||||
import de.mm20.launcher2.ui.common.FavoritesVM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import de.mm20.launcher2.widgets.FavoritesWidget
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class FavoritesWidgetVM : FavoritesVM() {
|
||||
|
||||
private val widgetsService: WidgetsService by inject()
|
||||
|
||||
override val tagsExpanded: Flow<Boolean> = dataStore.data.map { it.ui.widgetTagsMultiline }
|
||||
.shareIn(viewModelScope, SharingStarted.Lazily)
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
override val tagsExpanded: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
private val widget = MutableStateFlow<FavoritesWidget?>(null)
|
||||
|
||||
private val isTopWidget = widgetsService.isFavoritesWidgetFirst()
|
||||
private val clockWidgetFavSlots = dataStore.data.combine(isTopWidget) { data, isTop ->
|
||||
if (!isTop || !data.clockWidget.favoritesPart) 0
|
||||
else {
|
||||
if (data.clockWidget.layout == ClockWidgetLayout.Horizontal) data.grid.columnCount - 2
|
||||
else data.grid.columnCount
|
||||
private val clockWidgetFavSlots =
|
||||
combine(uiSettings.dock, isTopWidget, uiSettings.gridSettings) { (dock, isTop, grid) ->
|
||||
dock as Boolean
|
||||
isTop as Boolean
|
||||
grid as GridSettings
|
||||
if (!isTop || !dock) 0
|
||||
else {
|
||||
grid.columnCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val favorites = super.favorites.combine(clockWidgetFavSlots) { favs, slots ->
|
||||
if (selectedTag.value == null) {
|
||||
@@ -38,17 +40,17 @@ class FavoritesWidgetVM : FavoritesVM() {
|
||||
}
|
||||
|
||||
override fun setTagsExpanded(expanded: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setUi(
|
||||
it.ui.toBuilder()
|
||||
.setWidgetTagsMultiline(expanded)
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
this.tagsExpanded.value = expanded
|
||||
val widget = this.widget.value ?: return
|
||||
widgetsService.updateWidget(
|
||||
widget.copy(
|
||||
config = widget.config.copy(tagsMultiline = expanded)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun updateWidget(widget: FavoritesWidget) {
|
||||
tagsExpanded.value = widget.config.tagsMultiline
|
||||
}
|
||||
|
||||
}
|
||||
+3
-5
@@ -5,11 +5,10 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.*
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.weather.WeatherSettings
|
||||
import de.mm20.launcher2.weather.DailyForecast
|
||||
import de.mm20.launcher2.weather.Forecast
|
||||
import de.mm20.launcher2.weather.WeatherRepository
|
||||
import de.mm20.launcher2.weather.settings.WeatherSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -26,8 +25,6 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
/**
|
||||
* Index of the currently selected day in [dailyForecasts]
|
||||
*/
|
||||
@@ -111,7 +108,8 @@ class WeatherWidgetVM : ViewModel(), KoinComponent {
|
||||
val autoLocation = weatherSettings.autoLocation
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val imperialUnits = dataStore.data.map { it.weather.imperialUnits }
|
||||
val imperialUnits = weatherSettings.imperialUnits
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun selectDay(index: Int) {
|
||||
selectedDayIndex = min(index, forecasts.lastIndex)
|
||||
|
||||
@@ -5,8 +5,8 @@ import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.navigation.NavController
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.GridSettings
|
||||
import de.mm20.launcher2.preferences.ui.CardStyle
|
||||
import de.mm20.launcher2.preferences.ui.GridSettings
|
||||
import de.mm20.launcher2.ui.theme.WallpaperColors
|
||||
|
||||
val LocalWindowSize = compositionLocalOf { Size(0f, 0f) }
|
||||
@@ -15,11 +15,11 @@ val LocalAppWidgetHost = compositionLocalOf<AppWidgetHost?>(defaultFactory = { n
|
||||
|
||||
val LocalNavController = compositionLocalOf<NavController?> { null }
|
||||
|
||||
val LocalCardStyle = compositionLocalOf<Settings.CardSettings> { Settings.CardSettings.getDefaultInstance() }
|
||||
val LocalCardStyle = compositionLocalOf { CardStyle() }
|
||||
|
||||
val LocalFavoritesEnabled = compositionLocalOf { true }
|
||||
|
||||
val LocalGridSettings = compositionLocalOf { GridSettings.newBuilder().setColumnCount(5).setShowLabels(true).setIconSize(48).build() }
|
||||
val LocalGridSettings = compositionLocalOf { GridSettings() }
|
||||
|
||||
val LocalSnackbarHostState = compositionLocalOf { SnackbarHostState() }
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.backup.BackupSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
|
||||
@@ -170,9 +169,6 @@ class SettingsActivity : BaseActivity() {
|
||||
composable(ROUTE_MEDIA_INTEGRATION) {
|
||||
MediaIntegrationSettingsScreen()
|
||||
}
|
||||
composable("settings/homescreen/clock") {
|
||||
ClockWidgetSettingsScreen()
|
||||
}
|
||||
composable("settings/favorites") {
|
||||
FavoritesSettingsScreen()
|
||||
}
|
||||
|
||||
+13
-14
@@ -10,9 +10,8 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
|
||||
import de.mm20.launcher2.preferences.ColorScheme
|
||||
import de.mm20.launcher2.preferences.Font
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
@@ -28,22 +27,22 @@ fun AppearanceSettingsScreen() {
|
||||
val viewModel: AppearanceSettingsScreenVM = viewModel()
|
||||
val context = LocalContext.current
|
||||
val navController = LocalNavController.current
|
||||
val themeName by viewModel.colorSchemeName.collectAsStateWithLifecycle(null)
|
||||
val themeName by viewModel.themeName.collectAsStateWithLifecycle(null)
|
||||
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
val theme by viewModel.theme.collectAsState()
|
||||
val theme by viewModel.colorScheme.collectAsState()
|
||||
ListPreference(
|
||||
title = stringResource(id = R.string.preference_theme),
|
||||
items = listOf(
|
||||
stringResource(id = R.string.preference_theme_system) to Theme.System,
|
||||
stringResource(id = R.string.preference_theme_light) to Theme.Light,
|
||||
stringResource(id = R.string.preference_theme_dark) to Theme.Dark,
|
||||
stringResource(id = R.string.preference_theme_system) to ColorScheme.System,
|
||||
stringResource(id = R.string.preference_theme_light) to ColorScheme.Light,
|
||||
stringResource(id = R.string.preference_theme_dark) to ColorScheme.Dark,
|
||||
),
|
||||
value = theme,
|
||||
onValueChanged = { newValue ->
|
||||
if (newValue == null) return@ListPreference
|
||||
viewModel.setTheme(newValue)
|
||||
viewModel.setColorScheme(newValue)
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
@@ -54,13 +53,13 @@ fun AppearanceSettingsScreen() {
|
||||
}
|
||||
)
|
||||
if (isAtLeastApiLevel(31)) {
|
||||
val compatMode by viewModel.compatMode.collectAsState()
|
||||
val compatModeColors by viewModel.compatModeColors.collectAsState()
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.preference_force_compat_system_colors),
|
||||
summary = stringResource(id = R.string.preference_force_compat_system_colors_summary),
|
||||
value = compatMode,
|
||||
value = compatModeColors,
|
||||
onValueChanged = {
|
||||
viewModel.setCompatMode(it)
|
||||
viewModel.setCompatModeColors(it)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -68,8 +67,8 @@ fun AppearanceSettingsScreen() {
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_font),
|
||||
items = listOf(
|
||||
"Outfit" to AppearanceSettings.Font.Outfit,
|
||||
stringResource(R.string.preference_font_system) to AppearanceSettings.Font.SystemDefault,
|
||||
"Outfit" to Font.Outfit,
|
||||
stringResource(R.string.preference_font_system) to Font.System,
|
||||
),
|
||||
value = font,
|
||||
onValueChanged = {
|
||||
|
||||
+14
-41
@@ -3,74 +3,47 @@ package de.mm20.launcher2.ui.settings.appearance
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Font
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme
|
||||
import de.mm20.launcher2.preferences.ColorScheme
|
||||
import de.mm20.launcher2.preferences.Font
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import java.util.UUID
|
||||
|
||||
class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
private val iconService: IconService by inject()
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
|
||||
val theme = dataStore.data.map { it.appearance.theme }
|
||||
val colorScheme = uiSettings.colorScheme
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setTheme(theme: Theme) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(it.appearance.toBuilder().setTheme(theme))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
fun setColorScheme(colorScheme: ColorScheme) {
|
||||
uiSettings.setColorScheme(colorScheme)
|
||||
}
|
||||
|
||||
val colorSchemeName = dataStore.data.map {
|
||||
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let {
|
||||
UUID.fromString(it)
|
||||
}
|
||||
}
|
||||
.flatMapLatest {
|
||||
val themeName = uiSettings.theme.flatMapLatest {
|
||||
themeRepository.getThemeOrDefault(it)
|
||||
}.map {
|
||||
it.name
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val compatMode = dataStore.data.map {
|
||||
it.appearance.forceCompatModeSystemColors
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
val compatModeColors = uiSettings.compatModeColors
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun setCompatMode(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(it.appearance.toBuilder().setForceCompatModeSystemColors(enabled))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
fun setCompatModeColors(enabled: Boolean) {
|
||||
uiSettings.setCompatModeColors(enabled)
|
||||
}
|
||||
|
||||
val font = dataStore.data.map { it.appearance.font }
|
||||
val font = uiSettings.font
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setFont(font: Font) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(it.appearance.toBuilder().setFont(font))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setFont(font)
|
||||
}
|
||||
}
|
||||
-1
@@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings.buildinfo
|
||||
import androidx.lifecycle.ViewModel
|
||||
import de.mm20.launcher2.accounts.AccountType
|
||||
import de.mm20.launcher2.accounts.AccountsRepository
|
||||
import de.mm20.launcher2.preferences.Settings.WeatherSettings.WeatherProvider
|
||||
import de.mm20.launcher2.weather.WeatherRepository
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
@@ -15,7 +15,9 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.SurfaceShape
|
||||
import de.mm20.launcher2.preferences.ui.CardStyle
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.LauncherCard
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
@@ -26,6 +28,9 @@ import de.mm20.launcher2.ui.component.preferences.SliderPreference
|
||||
@Composable
|
||||
fun CardsSettingsScreen() {
|
||||
val viewModel: CardsSettingsScreenVM = viewModel()
|
||||
|
||||
val cardStyle by viewModel.cardStyle.collectAsState(CardStyle())
|
||||
|
||||
PreferenceScreen(title = stringResource(R.string.preference_cards)) {
|
||||
item {
|
||||
Box(
|
||||
@@ -43,23 +48,21 @@ fun CardsSettingsScreen() {
|
||||
}
|
||||
item {
|
||||
PreferenceCategory {
|
||||
val shape by viewModel.shape.collectAsState()
|
||||
ListPreference(
|
||||
icon = Icons.Rounded.Rectangle,
|
||||
title = stringResource(R.string.preference_cards_shape),
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_cards_shape_rounded) to Settings.CardSettings.Shape.Rounded,
|
||||
stringResource(R.string.preference_cards_shape_cut) to Settings.CardSettings.Shape.Cut,
|
||||
stringResource(R.string.preference_cards_shape_rounded) to SurfaceShape.Rounded,
|
||||
stringResource(R.string.preference_cards_shape_cut) to SurfaceShape.Cut,
|
||||
),
|
||||
value = shape,
|
||||
value = cardStyle.shape,
|
||||
onValueChanged = {
|
||||
if (it != null) viewModel.setShape(it)
|
||||
viewModel.setShape(it)
|
||||
})
|
||||
val radius by viewModel.radius.collectAsState()
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_cards_corner_radius),
|
||||
icon = Icons.Rounded.RoundedCorner,
|
||||
value = radius,
|
||||
value = cardStyle.cornerRadius,
|
||||
min = 0,
|
||||
max = 24,
|
||||
step = 1,
|
||||
@@ -67,22 +70,20 @@ fun CardsSettingsScreen() {
|
||||
viewModel.setRadius(it)
|
||||
}
|
||||
)
|
||||
val opacity by viewModel.opacity.collectAsState()
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_cards_opacity),
|
||||
icon = Icons.Rounded.Opacity,
|
||||
value = opacity,
|
||||
value = cardStyle.opacity,
|
||||
min = 0f,
|
||||
max = 1f,
|
||||
onValueChanged = {
|
||||
viewModel.setOpacity(it)
|
||||
}
|
||||
)
|
||||
val borderWidth by viewModel.borderWidth.collectAsState()
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_cards_stroke_width),
|
||||
icon = Icons.Rounded.LineWeight,
|
||||
value = borderWidth,
|
||||
value = cardStyle.borderWidth,
|
||||
min = 0,
|
||||
max = 8,
|
||||
step = 1,
|
||||
|
||||
@@ -1,70 +1,29 @@
|
||||
package de.mm20.launcher2.ui.settings.cards
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import de.mm20.launcher2.preferences.SurfaceShape
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class CardsSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
class CardsSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val cardStyle = uiSettings.cardStyle
|
||||
|
||||
val opacity = dataStore.data.map { it.cards.opacity }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0f)
|
||||
fun setOpacity(opacity: Float) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCards(it.cards.toBuilder()
|
||||
.setOpacity(opacity)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setCardOpacity(opacity)
|
||||
}
|
||||
|
||||
val radius = dataStore.data.map { it.cards.radius }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||
|
||||
fun setRadius(radius: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCards(it.cards.toBuilder()
|
||||
.setRadius(radius)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setCardRadius(radius)
|
||||
}
|
||||
|
||||
val borderWidth = dataStore.data.map { it.cards.borderWidth }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 0)
|
||||
fun setBorderWidth(borderWidth: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCards(it.cards.toBuilder()
|
||||
.setBorderWidth(borderWidth)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setCardBorderWidth(borderWidth)
|
||||
}
|
||||
|
||||
val shape = dataStore.data.map { it.cards.shape }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setShape(shape: Settings.CardSettings.Shape) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setCards(it.cards.toBuilder()
|
||||
.setShape(shape)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
fun setShape(shape: SurfaceShape) {
|
||||
uiSettings.setCardShape(shape)
|
||||
}
|
||||
}
|
||||
-204
@@ -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)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+28
-111
@@ -2,9 +2,10 @@ package de.mm20.launcher2.ui.settings.clockwidget
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings
|
||||
import de.mm20.launcher2.preferences.Settings.ClockWidgetSettings.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.ClockWidgetAlignment
|
||||
import de.mm20.launcher2.preferences.ClockWidgetColors
|
||||
import de.mm20.launcher2.preferences.ClockWidgetStyle
|
||||
import de.mm20.launcher2.preferences.ui.ClockWidgetSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -13,146 +14,62 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class ClockWidgetSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
val layout = dataStore.data.map { it.clockWidget.layout }
|
||||
private val settings: ClockWidgetSettings by inject()
|
||||
val compact = settings.compact
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setLayout(layout: ClockWidgetSettings.ClockWidgetLayout) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setLayout(layout)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
fun setCompact(compact: Boolean) {
|
||||
settings.setCompact(compact)
|
||||
}
|
||||
|
||||
val clockStyle = dataStore.data.map { it.clockWidget.clockStyle }
|
||||
val clockStyle = settings.clockStyle
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setClockStyle(clockStyle: ClockWidgetSettings.ClockStyle) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setClockStyle(clockStyle)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
fun setClockStyle(clockStyle: ClockWidgetStyle) {
|
||||
settings.setClockStyle(clockStyle)
|
||||
}
|
||||
|
||||
val color = dataStore.data.map { it.clockWidget.color }
|
||||
val color = settings.color
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setColor(color: ClockWidgetColors) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setColor(color)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setColor(color)
|
||||
}
|
||||
|
||||
val fillHeight = dataStore.data.map { it.clockWidget.fillHeight }
|
||||
val fillHeight = settings.fillHeight
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setFillHeight(fillHeight: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setFillHeight(fillHeight)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setFillHeight(fillHeight)
|
||||
}
|
||||
|
||||
val datePart = dataStore.data.map { it.clockWidget.datePart }
|
||||
val dock = settings.dock
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val parts = settings.parts
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setDatePart(datePart: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setDatePart(datePart)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setDatePart(datePart)
|
||||
}
|
||||
|
||||
val favoritesPart = dataStore.data.map { it.clockWidget.favoritesPart }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setFavoritesPart(favoritesPart: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setFavoritesPart(favoritesPart)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setDock(favoritesPart)
|
||||
}
|
||||
|
||||
val batteryPart = dataStore.data.map { it.clockWidget.batteryPart }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setBatteryPart(batteryPart: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setBatteryPart(batteryPart)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setBatteryPart(batteryPart)
|
||||
}
|
||||
|
||||
val musicPart = dataStore.data.map { it.clockWidget.musicPart }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setMusicPart(musicPart: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setMusicPart(musicPart)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setMusicPart(musicPart)
|
||||
}
|
||||
|
||||
val alarmPart = dataStore.data.map { it.clockWidget.alarmPart }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setAlarmPart(alarmPart: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setAlarmPart(alarmPart)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
settings.setAlarmPart(alarmPart)
|
||||
}
|
||||
|
||||
val alignment = dataStore.data.map { it.clockWidget.alignment }
|
||||
val alignment = settings.alignment
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setAlignment(alignment: ClockWidgetSettings.ClockWidgetAlignment) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setClockWidget(
|
||||
it.clockWidget.toBuilder()
|
||||
.setAlignment(alignment)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
fun setAlignment(alignment: ClockWidgetAlignment) {
|
||||
settings.setAlignment(alignment)
|
||||
}
|
||||
}
|
||||
+14
-21
@@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.colorscheme
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.protobuf.ByteString
|
||||
import de.mm20.launcher2.ktx.toBytes
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.ThemeDescriptor
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.BlackAndWhiteThemeId
|
||||
import de.mm20.launcher2.themes.DefaultThemeId
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.themes.fromJson
|
||||
import de.mm20.launcher2.themes.toJson
|
||||
import de.mm20.launcher2.ui.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -31,12 +27,14 @@ import java.util.UUID
|
||||
class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val themeRepository: ThemeRepository by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
|
||||
val selectedTheme: Flow<UUID?> = dataStore.data.map {
|
||||
it.appearance.themeId?.takeIf { it.isNotEmpty() }?.let {
|
||||
UUID.fromString(it)
|
||||
} ?: DefaultThemeId
|
||||
val selectedTheme = uiSettings.theme.map {
|
||||
when(it) {
|
||||
ThemeDescriptor.Default -> DefaultThemeId
|
||||
ThemeDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
|
||||
is ThemeDescriptor.Custom -> UUID.fromString(it.id)
|
||||
}
|
||||
}
|
||||
val themes: Flow<List<Theme>> = themeRepository.getThemes()
|
||||
|
||||
@@ -49,15 +47,10 @@ class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
}
|
||||
|
||||
fun selectTheme(theme: Theme) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder()
|
||||
.setThemeId(theme.id.toString())
|
||||
)
|
||||
.build()
|
||||
}
|
||||
when(theme.id) {
|
||||
DefaultThemeId -> ThemeDescriptor.Default
|
||||
BlackAndWhiteThemeId -> ThemeDescriptor.BlackAndWhite
|
||||
else -> ThemeDescriptor.Custom(theme.id.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-9
@@ -2,24 +2,20 @@ package de.mm20.launcher2.ui.settings.easteregg
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.IconShape
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class EasterEggSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val settings: UiSettings by inject()
|
||||
|
||||
val easterEgg = dataStore.data.map { it.easterEgg }
|
||||
val easterEgg = settings.iconShape.map { it == IconShape.EasterEgg }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
fun setEasterEgg(easterEgg: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setEasterEgg(easterEgg).build()
|
||||
}
|
||||
}
|
||||
settings.setIconShape(if (easterEgg) IconShape.EasterEgg else IconShape.PlatformDefault)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -14,7 +14,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor
|
||||
import de.mm20.launcher2.preferences.WeightFactor
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
|
||||
+13
-47
@@ -2,8 +2,9 @@ package de.mm20.launcher2.ui.settings.favorites
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.SearchResultOrderingSettings.WeightFactor
|
||||
import de.mm20.launcher2.preferences.WeightFactor
|
||||
import de.mm20.launcher2.preferences.search.FavoritesSettings
|
||||
import de.mm20.launcher2.preferences.search.RankingSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -12,65 +13,30 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class FavoritesSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val favoritesSettings: FavoritesSettings by inject()
|
||||
private val rankingSettings: RankingSettings by inject()
|
||||
|
||||
val frequentlyUsed = dataStore.data.map { it.favorites.frequentlyUsed }
|
||||
val frequentlyUsed = favoritesSettings.frequentlyUsed
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setFrequentlyUsed(frequentlyUsed: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFavorites(
|
||||
it.favorites.toBuilder()
|
||||
.setFrequentlyUsed(frequentlyUsed)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
favoritesSettings.setFrequentlyUsed(frequentlyUsed)
|
||||
}
|
||||
|
||||
val frequentlyUsedRows = dataStore.data.map { it.favorites.frequentlyUsedRows }
|
||||
val frequentlyUsedRows = favoritesSettings.frequentlyUsedRows
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
||||
fun setFrequentlyUsedRows(frequentlyUsedRows: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFavorites(
|
||||
it.favorites.toBuilder()
|
||||
.setFrequentlyUsedRows(frequentlyUsedRows)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
favoritesSettings.setFrequentlyUsedRows(frequentlyUsedRows)
|
||||
}
|
||||
|
||||
val editButton = dataStore.data.map { it.favorites.editButton }
|
||||
val editButton = favoritesSettings.showEditButton
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setEditButton(editButton: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setFavorites(
|
||||
it.favorites.toBuilder()
|
||||
.setEditButton(editButton)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
favoritesSettings.setShowEditButton(editButton)
|
||||
}
|
||||
|
||||
val searchResultWeightFactor = dataStore.data.map { it.resultOrdering.weightFactor }
|
||||
val searchResultWeightFactor = rankingSettings.weightFactor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), WeightFactor.Default)
|
||||
fun setSearchResultWeightFactor(searchResultWeightFactor: WeightFactor) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setResultOrdering(
|
||||
it.resultOrdering.toBuilder()
|
||||
.setWeightFactor(searchResultWeightFactor)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
rankingSettings.setWeightFactor(searchResultWeightFactor)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,11 +7,11 @@ import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.accounts.Account
|
||||
import de.mm20.launcher2.accounts.AccountType
|
||||
import de.mm20.launcher2.accounts.AccountsRepository
|
||||
import de.mm20.launcher2.files.settings.FileSearchSettings
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.plugin.PluginType
|
||||
import de.mm20.launcher2.plugins.PluginService
|
||||
import de.mm20.launcher2.preferences.search.FileSearchSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
+26
-25
@@ -27,8 +27,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout
|
||||
import de.mm20.launcher2.preferences.BaseLayout
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.preferences.LegacySettings.LayoutSettings.Layout
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.common.SearchablePicker
|
||||
@@ -47,14 +48,14 @@ fun GestureSettingsScreen() {
|
||||
val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null)
|
||||
|
||||
val options = buildList {
|
||||
add(stringResource(R.string.gesture_action_none) to GestureAction.None)
|
||||
add(stringResource(R.string.gesture_action_notifications) to GestureAction.OpenNotificationDrawer)
|
||||
add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.OpenQuickSettings)
|
||||
if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.LockScreen)
|
||||
add(stringResource(R.string.gesture_action_recents) to GestureAction.OpenRecents)
|
||||
add(stringResource(R.string.gesture_action_power_menu) to GestureAction.OpenPowerDialog)
|
||||
add(stringResource(R.string.gesture_action_open_search) to GestureAction.OpenSearch)
|
||||
add(stringResource(R.string.gesture_action_launch_app) to GestureAction.LaunchApp)
|
||||
add(stringResource(R.string.gesture_action_none) to GestureAction.NoAction)
|
||||
add(stringResource(R.string.gesture_action_notifications) to GestureAction.Notifications)
|
||||
add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.QuickSettings)
|
||||
if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.ScreenLock)
|
||||
add(stringResource(R.string.gesture_action_recents) to GestureAction.Recents)
|
||||
add(stringResource(R.string.gesture_action_power_menu) to GestureAction.PowerMenu)
|
||||
add(stringResource(R.string.gesture_action_open_search) to GestureAction.Search)
|
||||
add(stringResource(R.string.gesture_action_launch_app) to GestureAction.Launch(null))
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
@@ -64,9 +65,9 @@ fun GestureSettingsScreen() {
|
||||
val baseLayout by viewModel.baseLayout.collectAsStateWithLifecycle(null)
|
||||
ListPreference(title = stringResource(R.string.preference_layout_open_search),
|
||||
items = listOf(
|
||||
stringResource(R.string.open_search_pull_down) to Layout.PullDown,
|
||||
stringResource(R.string.open_search_swipe_left) to Layout.Pager,
|
||||
stringResource(R.string.open_search_swipe_right) to Layout.PagerReversed,
|
||||
stringResource(R.string.open_search_pull_down) to BaseLayout.PullDown,
|
||||
stringResource(R.string.open_search_swipe_left) to BaseLayout.Pager,
|
||||
stringResource(R.string.open_search_swipe_right) to BaseLayout.PagerReversed,
|
||||
),
|
||||
value = baseLayout,
|
||||
onValueChanged = {
|
||||
@@ -125,7 +126,7 @@ fun GestureSettingsScreen() {
|
||||
)
|
||||
|
||||
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
|
||||
val swipeDownIsSearch = layout == Layout.PullDown
|
||||
val swipeDownIsSearch = layout == BaseLayout.PullDown
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown) && !swipeDownIsSearch) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
@@ -149,7 +150,7 @@ fun GestureSettingsScreen() {
|
||||
)
|
||||
|
||||
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
|
||||
val swipeLeftIsSearch = layout == Layout.Pager
|
||||
val swipeLeftIsSearch = layout == BaseLayout.Pager
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft) && !swipeLeftIsSearch) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
@@ -173,7 +174,7 @@ fun GestureSettingsScreen() {
|
||||
)
|
||||
|
||||
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
|
||||
val swipeRightIsSearch = layout == Layout.PagerReversed
|
||||
val swipeRightIsSearch = layout == BaseLayout.PagerReversed
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight) && !swipeRightIsSearch) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
@@ -225,11 +226,11 @@ fun GestureSettingsScreen() {
|
||||
|
||||
fun requiresAccessibilityService(action: GestureAction?): Boolean {
|
||||
return when (action) {
|
||||
GestureAction.OpenNotificationDrawer,
|
||||
GestureAction.LockScreen,
|
||||
GestureAction.OpenQuickSettings,
|
||||
GestureAction.OpenRecents,
|
||||
GestureAction.OpenPowerDialog -> true
|
||||
is GestureAction.Notifications,
|
||||
is GestureAction.ScreenLock,
|
||||
is GestureAction.QuickSettings,
|
||||
is GestureAction.Recents,
|
||||
is GestureAction.PowerMenu -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@@ -256,12 +257,12 @@ fun GesturePreference(
|
||||
title = title,
|
||||
enabled = !isOpenSearch,
|
||||
items = options,
|
||||
value = if (isOpenSearch) GestureAction.OpenSearch else value,
|
||||
value = if (isOpenSearch) GestureAction.Search else value,
|
||||
onValueChanged = { if (it != null) onValueChanged(it) }
|
||||
)
|
||||
}
|
||||
|
||||
if (value == GestureAction.LaunchApp && !isOpenSearch) {
|
||||
if (value is GestureAction.Launch && !isOpenSearch) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
@@ -277,12 +278,12 @@ fun GesturePreference(
|
||||
}
|
||||
}
|
||||
|
||||
if (!isOpenSearch && value == GestureAction.LaunchApp && (showAppPicker || app == null)) {
|
||||
if (!isOpenSearch && value is GestureAction.Launch && (showAppPicker || app == null)) {
|
||||
SearchablePicker(
|
||||
title = { Text(title) },
|
||||
onDismissRequest = {
|
||||
showAppPicker = false
|
||||
if (app == null) onValueChanged(GestureAction.None)
|
||||
if (app == null) onValueChanged(GestureAction.NoAction)
|
||||
},
|
||||
value = app,
|
||||
onValueChanged = {
|
||||
|
||||
+53
-135
@@ -3,15 +3,16 @@ package de.mm20.launcher2.ui.settings.gestures
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.BaseLayout
|
||||
import de.mm20.launcher2.preferences.GestureAction
|
||||
import de.mm20.launcher2.preferences.ui.GestureSettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
@@ -22,7 +23,8 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val gestureSettings: GestureSettings by inject()
|
||||
private val uiSettings: UiSettings by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val searchableRepository: SavableSearchableRepository by inject()
|
||||
private val iconService: IconService by inject()
|
||||
@@ -30,208 +32,124 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val baseLayout = dataStore.data.map { it.layout.baseLayout }
|
||||
val baseLayout = uiSettings.baseLayout
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setBaseLayout(baseLayout: Settings.LayoutSettings.Layout) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setLayout(it.layout.toBuilder().setBaseLayout(baseLayout))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
fun setBaseLayout(baseLayout: BaseLayout) {
|
||||
uiSettings.setBaseLayout(baseLayout)
|
||||
}
|
||||
|
||||
val swipeDown = dataStore.data.map { it.gestures.swipeDown }
|
||||
|
||||
val swipeDown = gestureSettings.swipeDown
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val swipeLeft = dataStore.data.map { it.gestures.swipeLeft }
|
||||
val swipeLeft = gestureSettings.swipeLeft
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val swipeRight = dataStore.data.map { it.gestures.swipeRight }
|
||||
val swipeRight = gestureSettings.swipeRight
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val doubleTap = dataStore.data.map { it.gestures.doubleTap }
|
||||
val doubleTap = gestureSettings.doubleTap
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val longPress = dataStore.data.map { it.gestures.longPress }
|
||||
val longPress = gestureSettings.longPress
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val homeButton = dataStore.data.map { it.gestures.homeButton }
|
||||
val homeButton = gestureSettings.homeButton
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setSwipeDown(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeDown(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setSwipeDown(action)
|
||||
}
|
||||
|
||||
fun setSwipeLeft(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeLeft(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setSwipeLeft(action)
|
||||
}
|
||||
|
||||
fun setSwipeRight(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeRight(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setSwipeRight(action)
|
||||
}
|
||||
|
||||
fun setDoubleTap(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setDoubleTap(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setDoubleTap(action)
|
||||
}
|
||||
|
||||
fun setLongPress(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setLongPress(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setLongPress(action)
|
||||
}
|
||||
|
||||
fun setHomeButton(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setHomeButton(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
gestureSettings.setHomeButton(action)
|
||||
}
|
||||
|
||||
val swipeLeftApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeLeftApp }
|
||||
val swipeLeftApp: Flow<SavableSearchable?> = swipeLeft
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setSwipeLeftApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setSwipeLeftApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setSwipeLeft(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
val swipeRightApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeRightApp }
|
||||
val swipeRightApp: Flow<SavableSearchable?> = swipeRight
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setSwipeRightApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setSwipeRightApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setSwipeRight(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
val swipeDownApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.swipeDownApp }
|
||||
val swipeDownApp: Flow<SavableSearchable?> = swipeDown
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setSwipeDownApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setSwipeDownApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setSwipeDown(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
val longPressApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.longPressApp }
|
||||
val longPressApp: Flow<SavableSearchable?> = longPress
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setLongPressApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setLongPressApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setLongPress(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
val doubleTapApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.doubleTapApp }
|
||||
val doubleTapApp: Flow<SavableSearchable?> = doubleTap
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setDoubleTapApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setDoubleTapApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setDoubleTap(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
val homeButtonApp: Flow<SavableSearchable?> = dataStore.data.map { it.gestures.homeButtonApp }
|
||||
val homeButtonApp: Flow<SavableSearchable?> = homeButton
|
||||
.map {
|
||||
if (it.isEmpty()) null else searchableRepository.getByKeys(listOf(it)).firstOrNull()
|
||||
if (it !is GestureAction.Launch || it.key == null) null
|
||||
else searchableRepository.getByKeys(listOf(it.key!!)).firstOrNull()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
||||
|
||||
fun setHomeButtonApp(searchable: SavableSearchable?) {
|
||||
viewModelScope.launch {
|
||||
searchable?.let { searchableRepository.insert(it) }
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGestures(it.gestures.toBuilder()
|
||||
.setHomeButtonApp(searchable?.key ?: "")
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
||||
setHomeButton(GestureAction.Launch(searchable.key))
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun requestPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
|
||||
}
|
||||
|
||||
+4
-10
@@ -9,7 +9,7 @@ import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import de.mm20.launcher2.search.SavableSearchable
|
||||
import de.mm20.launcher2.search.Application
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -29,7 +29,7 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val appRepository: AppRepository by inject()
|
||||
private val searchableRepository: SavableSearchableRepository by inject()
|
||||
private val iconService: IconService by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val searchUiSettings: SearchUiSettings by inject()
|
||||
|
||||
val allApps = appRepository.findMany().map {
|
||||
withContext(Dispatchers.Default) { it.sorted() }
|
||||
@@ -68,16 +68,10 @@ class HiddenItemsSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
app.openAppDetails(context)
|
||||
}
|
||||
|
||||
val hiddenItemsButton = dataStore.data.map { it.searchBar.hiddenItemsButton }
|
||||
val hiddenItemsButton = searchUiSettings.hiddenItemsButton
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun setHiddenItemsButton(hidden: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSearchBar(it.searchBar.toBuilder().setHiddenItemsButton(hidden)).build()
|
||||
|
||||
}
|
||||
}
|
||||
searchUiSettings.setHiddenItemsButton(hidden)
|
||||
}
|
||||
}
|
||||
+17
-15
@@ -26,7 +26,10 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.google.accompanist.pager.HorizontalPagerIndicator
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||
import de.mm20.launcher2.preferences.SystemBarColors
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.SearchBar
|
||||
import de.mm20.launcher2.ui.component.SearchBarLevel
|
||||
@@ -109,14 +112,14 @@ fun HomescreenSettingsScreen() {
|
||||
viewModel.setSearchBarStyle(it)
|
||||
}
|
||||
)
|
||||
AnimatedVisibility(searchBarStyle == Settings.SearchBarSettings.SearchBarStyle.Transparent) {
|
||||
AnimatedVisibility(searchBarStyle == SearchBarStyle.Transparent) {
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_search_bar_color),
|
||||
value = searchBarColor,
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SearchBarSettings.SearchBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to Settings.SearchBarSettings.SearchBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SearchBarSettings.SearchBarColors.Dark,
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to SearchBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to SearchBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to SearchBarColors.Dark,
|
||||
),
|
||||
onValueChanged = {
|
||||
if (it != null) viewModel.setSearchBarColor(it)
|
||||
@@ -208,9 +211,9 @@ fun HomescreenSettingsScreen() {
|
||||
title = stringResource(R.string.preference_status_bar_icons),
|
||||
value = lightStatusBar,
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark,
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark,
|
||||
),
|
||||
onValueChanged = {
|
||||
if (it != null) viewModel.setLightStatusBar(it)
|
||||
@@ -220,9 +223,9 @@ fun HomescreenSettingsScreen() {
|
||||
title = stringResource(R.string.preference_nav_bar_icons),
|
||||
value = lightNavBar,
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to Settings.SystemBarsSettings.SystemBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to Settings.SystemBarsSettings.SystemBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to Settings.SystemBarsSettings.SystemBarColors.Dark,
|
||||
stringResource(R.string.preference_system_bar_icons_auto) to SystemBarColors.Auto,
|
||||
stringResource(R.string.preference_system_bar_icons_light) to SystemBarColors.Light,
|
||||
stringResource(R.string.preference_system_bar_icons_dark) to SystemBarColors.Dark,
|
||||
),
|
||||
onValueChanged = {
|
||||
if (it != null) viewModel.setLightNavBar(it)
|
||||
@@ -255,15 +258,14 @@ fun HomescreenSettingsScreen() {
|
||||
fun SearchBarStylePreference(
|
||||
title: String,
|
||||
summary: String? = null,
|
||||
value: Settings.SearchBarSettings.SearchBarStyle?,
|
||||
onValueChanged: (Settings.SearchBarSettings.SearchBarStyle) -> Unit
|
||||
value: SearchBarStyle?,
|
||||
onValueChanged: (SearchBarStyle) -> Unit
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
Preference(title = title, summary = summary, onClick = { showDialog = true })
|
||||
if (showDialog && value != null) {
|
||||
val styles = remember {
|
||||
Settings.SearchBarSettings.SearchBarStyle.values()
|
||||
.filter { it != Settings.SearchBarSettings.SearchBarStyle.UNRECOGNIZED }
|
||||
SearchBarStyle.entries
|
||||
}
|
||||
val pagerState = rememberPagerState(initialPage = styles.indexOf(value)) { styles.size }
|
||||
|
||||
|
||||
+53
-150
@@ -13,8 +13,12 @@ import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.ScreenOrientation
|
||||
import de.mm20.launcher2.preferences.SearchBarColors
|
||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||
import de.mm20.launcher2.preferences.SystemBarColors
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -23,52 +27,31 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
class HomescreenSettingsScreenVM(
|
||||
private val dataStore: LauncherDataStore,
|
||||
private val uiSettings: UiSettings,
|
||||
) : ViewModel() {
|
||||
|
||||
var showClockWidgetSheet by mutableStateOf(false)
|
||||
|
||||
|
||||
val dimWallpaper = dataStore.data.map { it.appearance.dimWallpaper }
|
||||
val dimWallpaper = uiSettings.dimWallpaper
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun setDimWallpaper(dimWallpaper: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder()
|
||||
.setDimWallpaper(dimWallpaper)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setDimWallpaper(dimWallpaper)
|
||||
}
|
||||
|
||||
val blurWallpaper = dataStore.data.map { it.appearance.blurWallpaper }
|
||||
val blurWallpaper = uiSettings.blurWallpaper
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
fun setBlurWallpaper(blurWallpaper: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder()
|
||||
.setBlurWallpaper(blurWallpaper)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setBlurWallpaper(blurWallpaper)
|
||||
}
|
||||
|
||||
val blurWallpaperRadius = dataStore.data.map { it.appearance.blurWallpaperRadius }
|
||||
val blurWallpaperRadius = uiSettings.wallpaperBlurRadius
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 32)
|
||||
|
||||
fun setBlurWallpaperRadius(blurWallpaperRadius: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAppearance(
|
||||
it.appearance.toBuilder()
|
||||
.setBlurWallpaperRadius(blurWallpaperRadius)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
uiSettings.setWallpaperBlurRadius(blurWallpaperRadius)
|
||||
}
|
||||
|
||||
fun openWallpaperChooser(context: AppCompatActivity) {
|
||||
@@ -80,168 +63,88 @@ class HomescreenSettingsScreenVM(
|
||||
return context.getSystemService<WindowManager>()?.isCrossWindowBlurEnabled == true
|
||||
}
|
||||
|
||||
val statusBarIcons = dataStore.data.map { it.systemBars.statusBarColor }
|
||||
val statusBarIcons = uiSettings.statusBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setLightStatusBar(statusBarColor: Settings.SystemBarsSettings.SystemBarColors) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSystemBars(
|
||||
it.systemBars.toBuilder()
|
||||
.setStatusBarColor(statusBarColor)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun setLightStatusBar(statusBarColor: SystemBarColors) {
|
||||
uiSettings.setStatusBarColor(statusBarColor)
|
||||
}
|
||||
|
||||
val navBarIcons = dataStore.data.map { it.systemBars.navBarColor }
|
||||
val navBarIcons = uiSettings.navigationBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setLightNavBar(navBarColors: Settings.SystemBarsSettings.SystemBarColors) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSystemBars(
|
||||
it.systemBars.toBuilder()
|
||||
.setNavBarColor(navBarColors)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun setLightNavBar(navBarColors: SystemBarColors) {
|
||||
uiSettings.setNavigationBarColor(navBarColors)
|
||||
}
|
||||
|
||||
val hideStatusBar = dataStore.data.map { it.systemBars.hideStatusBar }
|
||||
val hideStatusBar = uiSettings.hideStatusBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setHideStatusBar(hideStatusBar: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSystemBars(
|
||||
it.systemBars.toBuilder()
|
||||
.setHideStatusBar(hideStatusBar)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setHideStatusBar(hideStatusBar)
|
||||
}
|
||||
|
||||
val hideNavBar = dataStore.data.map { it.systemBars.hideNavBar }
|
||||
val hideNavBar = uiSettings.hideNavigationBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setHideNavBar(hideNavBar: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSystemBars(
|
||||
it.systemBars.toBuilder()
|
||||
.setHideNavBar(hideNavBar)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setHideNavigationBar(hideNavBar)
|
||||
}
|
||||
|
||||
val searchBarColor = dataStore.data.map { it.searchBar.color }
|
||||
val searchBarColor = uiSettings.searchBarColor
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setSearchBarColor(color: Settings.SearchBarSettings.SearchBarColors) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSearchBar(
|
||||
it.searchBar.toBuilder()
|
||||
.setColor(color)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSearchBarColor(color: SearchBarColors) {
|
||||
uiSettings.setSearchBarColor(color)
|
||||
}
|
||||
|
||||
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }
|
||||
val searchBarStyle = uiSettings.searchBarStyle
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setSearchBarStyle(searchBarStyle: Settings.SearchBarSettings.SearchBarStyle) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setSearchBar(
|
||||
it.searchBar.toBuilder()
|
||||
.setSearchBarStyle(searchBarStyle)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSearchBarStyle(searchBarStyle: SearchBarStyle) {
|
||||
uiSettings.setSearchBarStyle(searchBarStyle)
|
||||
}
|
||||
|
||||
val fixedSearchBar = dataStore.data.map { it.layout.fixedSearchBar }
|
||||
val fixedSearchBar = uiSettings.fixedSearchBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setFixedSearchBar(fixedSearchBar: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setLayout(it.layout.toBuilder().setFixedSearchBar(fixedSearchBar))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setFixedSearchBar(fixedSearchBar)
|
||||
}
|
||||
|
||||
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }
|
||||
val bottomSearchBar = uiSettings.bottomSearchBar
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setBottomSearchBar(bottomSearchBar: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setLayout(it.layout.toBuilder().setBottomSearchBar(bottomSearchBar))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setBottomSearchBar(bottomSearchBar)
|
||||
}
|
||||
|
||||
val fixedRotation = dataStore.data.map { it.layout.fixedRotation }
|
||||
val fixedRotation = uiSettings.orientation.map { it != ScreenOrientation.Auto }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setFixedRotation(fixedRotation: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setLayout(it.layout.toBuilder().setFixedRotation(fixedRotation))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setOrientation(if (fixedRotation) ScreenOrientation.Portrait else ScreenOrientation.Auto)
|
||||
}
|
||||
|
||||
val widgetEditButton = dataStore.data.map { it.widgets.editButton }
|
||||
val widgetEditButton = uiSettings.widgetEditButton
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setWidgetEditButton(editButton: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWidgets(
|
||||
it.widgets.toBuilder()
|
||||
.setEditButton(editButton)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setWidgetEditButton(editButton)
|
||||
}
|
||||
|
||||
val chargingAnimation = dataStore.data.map { it.animations.charging }
|
||||
val chargingAnimation = uiSettings.chargingAnimation
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setChargingAnimation(chargingAnimation: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setAnimations(
|
||||
it.animations.toBuilder()
|
||||
.setCharging(chargingAnimation)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setChargingAnimation(chargingAnimation)
|
||||
}
|
||||
|
||||
companion object : KoinComponent {
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
HomescreenSettingsScreenVM(
|
||||
dataStore = get()
|
||||
uiSettings = get()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import androidx.compose.material3.FilledIconToggleButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.PlainTooltipBox
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
@@ -50,7 +49,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.icons.StaticIconLayer
|
||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.IconShape
|
||||
import de.mm20.launcher2.preferences.ui.GridSettings
|
||||
import de.mm20.launcher2.preferences.ui.IconSettings
|
||||
import de.mm20.launcher2.preferences.ui.IconSettingsData
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||
@@ -63,25 +65,18 @@ import de.mm20.launcher2.ui.component.preferences.SliderPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.label
|
||||
import de.mm20.launcher2.ui.component.preferences.value
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
|
||||
@Composable
|
||||
fun IconsSettingsScreen() {
|
||||
val viewModel: IconsSettingsScreenVM = viewModel(factory = IconsSettingsScreenVM.Factory)
|
||||
val context = LocalContext.current
|
||||
|
||||
val iconSize by viewModel.iconSize.collectAsStateWithLifecycle(48)
|
||||
val grid by viewModel.grid.collectAsStateWithLifecycle(GridSettings())
|
||||
val icons by viewModel.icons.collectAsStateWithLifecycle(null)
|
||||
val density = LocalDensity.current
|
||||
val showLabels by viewModel.showLabels.collectAsStateWithLifecycle(null)
|
||||
val columnCount by viewModel.columnCount.collectAsStateWithLifecycle(5)
|
||||
val iconShape by viewModel.iconShape.collectAsStateWithLifecycle(Settings.IconSettings.IconShape.PlatformDefault)
|
||||
val adaptifyLegacyIcons by viewModel.adaptifyLegacyIcons.collectAsStateWithLifecycle(null)
|
||||
val themedIcons by viewModel.themedIcons.collectAsStateWithLifecycle(null)
|
||||
val iconShape by viewModel.iconShape.collectAsStateWithLifecycle(IconShape.PlatformDefault)
|
||||
|
||||
val iconPackPackage by viewModel.iconPack.collectAsStateWithLifecycle(null)
|
||||
val iconPackThemed by viewModel.iconPackThemed.collectAsState(true)
|
||||
val installedIconPacks by viewModel.installedIconPacks.collectAsState(emptyList())
|
||||
val forceThemedIcons by viewModel.forceThemedIcons.collectAsStateWithLifecycle(null)
|
||||
|
||||
val hasNotificationsPermission by viewModel.hasNotificationsPermission.collectAsStateWithLifecycle(null)
|
||||
|
||||
@@ -91,8 +86,8 @@ fun IconsSettingsScreen() {
|
||||
val shortcutBadges by viewModel.shortcutBadges.collectAsStateWithLifecycle(null)
|
||||
val pluginBadges by viewModel.pluginBadges.collectAsStateWithLifecycle(null)
|
||||
|
||||
val previewIcons by remember(iconSize) {
|
||||
viewModel.getPreviewIcons(with(density) { iconSize.dp.toPx() }.toInt())
|
||||
val previewIcons by remember(grid?.iconSize) {
|
||||
viewModel.getPreviewIcons(with(density) { grid.iconSize.dp.toPx() }.toInt())
|
||||
}.collectAsState(
|
||||
emptyList()
|
||||
)
|
||||
@@ -102,7 +97,7 @@ fun IconsSettingsScreen() {
|
||||
PreferenceCategory(title = stringResource(R.string.preference_category_grid)) {
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_grid_icon_size),
|
||||
value = iconSize,
|
||||
value = grid.iconSize,
|
||||
step = 8,
|
||||
min = 32,
|
||||
max = 64,
|
||||
@@ -113,14 +108,14 @@ fun IconsSettingsScreen() {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_grid_labels),
|
||||
summary = stringResource(R.string.preference_grid_labels_summary),
|
||||
value = showLabels == true,
|
||||
value = grid.showLabels,
|
||||
onValueChanged = {
|
||||
viewModel.setShowLabels(it)
|
||||
}
|
||||
)
|
||||
SliderPreference(
|
||||
title = stringResource(R.string.preference_grid_column_count),
|
||||
value = columnCount,
|
||||
value = grid.columnCount,
|
||||
min = 3,
|
||||
max = 12,
|
||||
onValueChanged = {
|
||||
@@ -148,7 +143,7 @@ fun IconsSettingsScreen() {
|
||||
modifier = Modifier.weight(1f),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
ShapedLauncherIcon(size = iconSize.dp, icon = { icon })
|
||||
ShapedLauncherIcon(size = grid.iconSize.dp, icon = { icon })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +160,7 @@ fun IconsSettingsScreen() {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_enforce_icon_shape),
|
||||
summary = stringResource(R.string.preference_enforce_icon_shape_summary),
|
||||
value = adaptifyLegacyIcons == true,
|
||||
value = icons?.adaptify == true,
|
||||
onValueChanged = {
|
||||
viewModel.setAdaptifyLegacyIcons(it)
|
||||
}
|
||||
@@ -173,7 +168,7 @@ fun IconsSettingsScreen() {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_themed_icons),
|
||||
summary = stringResource(R.string.preference_themed_icons_summary),
|
||||
value = themedIcons == true,
|
||||
value = icons?.themedIcons == true,
|
||||
onValueChanged = {
|
||||
viewModel.setThemedIcons(it)
|
||||
}
|
||||
@@ -181,14 +176,14 @@ fun IconsSettingsScreen() {
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_force_themed_icons),
|
||||
summary = stringResource(R.string.preference_force_themed_icons_summary),
|
||||
value = forceThemedIcons == true,
|
||||
enabled = themedIcons == true,
|
||||
value = icons?.forceThemed == true,
|
||||
enabled = icons?.themedIcons == true,
|
||||
onValueChanged = {
|
||||
viewModel.setForceThemedIcons(it)
|
||||
}
|
||||
)
|
||||
val iconPack by remember {
|
||||
derivedStateOf { installedIconPacks.firstOrNull { it.packageName == iconPackPackage } }
|
||||
derivedStateOf { installedIconPacks.firstOrNull { it.packageName == icons?.iconPack } }
|
||||
}
|
||||
val items = installedIconPacks.map {
|
||||
it.name to it
|
||||
@@ -265,7 +260,7 @@ fun IconsSettingsScreen() {
|
||||
PlainTooltipBox(tooltip = { Text(stringResource(R.string.icon_pack_dynamic_colors)) }) {
|
||||
FilledIconToggleButton(
|
||||
modifier = Modifier.tooltipTrigger(),
|
||||
checked = iconPackThemed,
|
||||
checked = icons?.iconPackThemed == true,
|
||||
onCheckedChange = {
|
||||
viewModel.setIconPackThemed(it)
|
||||
}) {
|
||||
@@ -347,16 +342,16 @@ fun IconsSettingsScreen() {
|
||||
fun IconShapePreference(
|
||||
title: String,
|
||||
summary: String? = null,
|
||||
value: Settings.IconSettings.IconShape?,
|
||||
onValueChanged: (Settings.IconSettings.IconShape) -> Unit
|
||||
value: IconShape?,
|
||||
onValueChanged: (IconShape) -> Unit
|
||||
) {
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
Preference(title = title, summary = summary, onClick = { showDialog = true })
|
||||
|
||||
if (showDialog && value != null) {
|
||||
val shapes = remember {
|
||||
Settings.IconSettings.IconShape.values()
|
||||
.filter { it != Settings.IconSettings.IconShape.UNRECOGNIZED && it != Settings.IconSettings.IconShape.EasterEgg }
|
||||
IconShape.entries
|
||||
.filter { it != IconShape.EasterEgg }
|
||||
}
|
||||
Dialog(onDismissRequest = { showDialog = false }) {
|
||||
Surface(
|
||||
@@ -431,19 +426,19 @@ fun IconShapePreference(
|
||||
|
||||
|
||||
@Composable
|
||||
private fun getShapeName(shape: Settings.IconSettings.IconShape?): String? {
|
||||
private fun getShapeName(shape: IconShape?): String? {
|
||||
return stringResource(
|
||||
when (shape) {
|
||||
Settings.IconSettings.IconShape.Triangle -> R.string.preference_icon_shape_triangle
|
||||
Settings.IconSettings.IconShape.Hexagon -> R.string.preference_icon_shape_hexagon
|
||||
Settings.IconSettings.IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square
|
||||
Settings.IconSettings.IconShape.Squircle -> R.string.preference_icon_shape_squircle
|
||||
Settings.IconSettings.IconShape.Square -> R.string.preference_icon_shape_square
|
||||
Settings.IconSettings.IconShape.Pentagon -> R.string.preference_icon_shape_pentagon
|
||||
Settings.IconSettings.IconShape.PlatformDefault -> R.string.preference_icon_shape_platform
|
||||
Settings.IconSettings.IconShape.Circle -> R.string.preference_icon_shape_circle
|
||||
Settings.IconSettings.IconShape.Teardrop -> R.string.preference_icon_shape_teardrop
|
||||
Settings.IconSettings.IconShape.Pebble -> R.string.preference_icon_shape_pebble
|
||||
IconShape.Triangle -> R.string.preference_icon_shape_triangle
|
||||
IconShape.Hexagon -> R.string.preference_icon_shape_hexagon
|
||||
IconShape.RoundedSquare -> R.string.preference_icon_shape_rounded_square
|
||||
IconShape.Squircle -> R.string.preference_icon_shape_squircle
|
||||
IconShape.Square -> R.string.preference_icon_shape_square
|
||||
IconShape.Pentagon -> R.string.preference_icon_shape_pentagon
|
||||
IconShape.PlatformDefault -> R.string.preference_icon_shape_platform
|
||||
IconShape.Circle -> R.string.preference_icon_shape_circle
|
||||
IconShape.Teardrop -> R.string.preference_icon_shape_teardrop
|
||||
IconShape.Pebble -> R.string.preference_icon_shape_pebble
|
||||
else -> return null
|
||||
}
|
||||
)
|
||||
|
||||
+24
-104
@@ -2,124 +2,64 @@ package de.mm20.launcher2.ui.settings.icons
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import de.mm20.launcher2.badges.settings.BadgeSettings
|
||||
import de.mm20.launcher2.icons.IconPack
|
||||
import de.mm20.launcher2.icons.IconService
|
||||
import de.mm20.launcher2.icons.LauncherIcon
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.search.Application
|
||||
import de.mm20.launcher2.preferences.IconShape
|
||||
import de.mm20.launcher2.preferences.ui.BadgeSettings
|
||||
import de.mm20.launcher2.preferences.ui.IconSettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.services.favorites.FavoritesService
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
class IconsSettingsScreenVM(
|
||||
private val dataStore: LauncherDataStore,
|
||||
private val uiSettings: UiSettings,
|
||||
private val iconSettings: IconSettings,
|
||||
private val badgeSettings: BadgeSettings,
|
||||
private val iconService: IconService,
|
||||
private val favoritesService: FavoritesService,
|
||||
private val permissionsManager: PermissionsManager,
|
||||
) : ViewModel() {
|
||||
|
||||
val grid = uiSettings.gridSettings
|
||||
|
||||
val columnCount = dataStore.data.map { it.grid.columnCount }
|
||||
fun setColumnCount(columnCount: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGrid(it.grid.toBuilder().setColumnCount(columnCount))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setGridColumnCount(columnCount)
|
||||
}
|
||||
|
||||
val iconSize = dataStore.data.map { it.grid.iconSize }
|
||||
fun setIconSize(iconSize: Int) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGrid(it.grid.toBuilder().setIconSize(iconSize))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setGridIconSize(iconSize)
|
||||
}
|
||||
|
||||
|
||||
val showLabels = dataStore.data.map { it.grid.showLabels }
|
||||
fun setShowLabels(showLabels: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setGrid(it.grid.toBuilder().setShowLabels(showLabels))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
uiSettings.setGridShowLabels(showLabels)
|
||||
}
|
||||
|
||||
val iconShape = dataStore.data.map { it.icons.shape }
|
||||
fun setIconShape(iconShape: Settings.IconSettings.IconShape) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons.toBuilder()
|
||||
.setShape(iconShape)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
val iconShape = uiSettings.iconShape
|
||||
fun setIconShape(iconShape: IconShape) {
|
||||
uiSettings.setIconShape(iconShape)
|
||||
}
|
||||
|
||||
val adaptifyLegacyIcons = dataStore.data.map { it.icons.adaptify }
|
||||
val icons = iconSettings
|
||||
fun setAdaptifyLegacyIcons(adaptify: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons.toBuilder()
|
||||
.setAdaptify(adaptify)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
iconSettings.setAdaptifyLegacyIcons(adaptify)
|
||||
}
|
||||
|
||||
val themedIcons = dataStore.data.map { it.icons.themedIcons }
|
||||
fun setThemedIcons(themedIcons: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons.toBuilder()
|
||||
.setThemedIcons(themedIcons)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
iconSettings.setThemedIcons(themedIcons)
|
||||
}
|
||||
|
||||
val forceThemedIcons = dataStore.data.map { it.icons.forceThemed }
|
||||
fun setForceThemedIcons(forceThemedIcons: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons.toBuilder()
|
||||
.setForceThemed(forceThemedIcons)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
iconSettings.setForceThemedIcons(forceThemedIcons)
|
||||
}
|
||||
|
||||
val installedIconPacks: Flow<List<IconPack>> = iconService.getInstalledIconPacks().map {
|
||||
@@ -132,33 +72,12 @@ class IconsSettingsScreenVM(
|
||||
) + it
|
||||
}
|
||||
|
||||
val iconPackThemed = dataStore.data.map { it.icons.iconPackThemed }
|
||||
fun setIconPackThemed(iconPackThemed: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons
|
||||
.toBuilder()
|
||||
.setIconPackThemed(iconPackThemed)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
iconSettings.setIconPackThemed(iconPackThemed)
|
||||
}
|
||||
|
||||
val iconPack = dataStore.data.map { it.icons.iconPack }
|
||||
fun setIconPack(iconPack: String) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setIcons(
|
||||
it.icons.toBuilder()
|
||||
.setIconPack(iconPack)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
fun setIconPack(iconPack: String?) {
|
||||
iconSettings.setIconPack(iconPack?.takeIf { it.isNotBlank() })
|
||||
}
|
||||
|
||||
val hasNotificationsPermission = permissionsManager.hasPermission(PermissionGroup.Notifications)
|
||||
@@ -193,10 +112,10 @@ class IconsSettingsScreenVM(
|
||||
}
|
||||
|
||||
fun getPreviewIcons(size: Int): Flow<List<LauncherIcon?>> {
|
||||
return columnCount.flatMapLatest { cols ->
|
||||
return grid.flatMapLatest { grid ->
|
||||
favoritesService.getFavorites(
|
||||
includeTypes = listOf("app"),
|
||||
limit = cols,
|
||||
limit = grid.columnCount,
|
||||
manuallySorted = true,
|
||||
automaticallySorted = true,
|
||||
frequentlyUsed = true,
|
||||
@@ -214,11 +133,12 @@ class IconsSettingsScreenVM(
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
IconsSettingsScreenVM(
|
||||
dataStore = get(),
|
||||
uiSettings = get(),
|
||||
iconService = get(),
|
||||
permissionsManager = get(),
|
||||
favoritesService = get(),
|
||||
badgeSettings = get(),
|
||||
iconSettings = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-19
@@ -11,13 +11,12 @@ import de.mm20.launcher2.ktx.normalize
|
||||
import de.mm20.launcher2.music.MusicService
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.media.MediaSettings
|
||||
import de.mm20.launcher2.search.AppProfile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
@@ -29,7 +28,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val musicService: MusicService by inject()
|
||||
private val appRepository: AppRepository by inject()
|
||||
private val iconService: IconService by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
private val mediaSettings: MediaSettings by inject()
|
||||
|
||||
val hasPermission =
|
||||
permissionsManager.hasPermission(PermissionGroup.Notifications)
|
||||
|
||||
@@ -52,9 +53,9 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
val musicApps = musicService.getInstalledPlayerPackages()
|
||||
val allApps = appRepository.findMany().first().filter { it.profile == AppProfile.Personal }
|
||||
.distinctBy { it.componentName.packageName }
|
||||
val settings = dataStore.data.map { it.musicWidget }.first()
|
||||
val allowList = settings.allowListList
|
||||
val denyList = settings.denyListList
|
||||
val settings = mediaSettings.first()
|
||||
val allowList = settings.allowList
|
||||
val denyList = settings.denyList
|
||||
|
||||
appList.value = allApps.map {
|
||||
AppListItem(
|
||||
@@ -92,19 +93,7 @@ class MediaIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
denyList.add(app.packageName)
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setMusicWidget(
|
||||
it.musicWidget.toBuilder()
|
||||
.clearAllowList()
|
||||
.addAllAllowList(allowList)
|
||||
.clearDenyList()
|
||||
.addAllDenyList(denyList)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
mediaSettings.setLists(allowList.toSet(), denyList.toSet())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.rounded.InsertDriveFile
|
||||
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
import androidx.compose.material.icons.rounded.Error
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
@@ -40,6 +41,7 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -48,7 +50,9 @@ import coil.compose.AsyncImage
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.plugin.PluginState
|
||||
import de.mm20.launcher2.plugin.PluginType
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.SwitchPreference
|
||||
import de.mm20.launcher2.ui.locals.LocalNavController
|
||||
@@ -76,6 +80,11 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
minActiveState = Lifecycle.State.RESUMED
|
||||
)
|
||||
|
||||
val weatherPlugins by viewModel.weatherPlugins.collectAsStateWithLifecycle(
|
||||
emptyList(),
|
||||
minActiveState = Lifecycle.State.RESUMED
|
||||
)
|
||||
|
||||
val requestPermissionStarter =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
@@ -87,6 +96,10 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
null
|
||||
)
|
||||
|
||||
val weatherProviderId by viewModel.weatherProvider.collectAsStateWithLifecycle(
|
||||
null
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
@@ -286,7 +299,7 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
AnimatedVisibility(pluginPackage?.enabled == true && hasPermission == true) {
|
||||
if (filePlugins.isNotEmpty()) {
|
||||
PreferenceCategory(
|
||||
"File search",
|
||||
stringResource(R.string.plugin_type_filesearch),
|
||||
iconPadding = false,
|
||||
) {
|
||||
for (plugin in filePlugins) {
|
||||
@@ -294,7 +307,7 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
if (state is PluginState.SetupRequired) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = state.message ?: "You need to setup this plugin first",
|
||||
text = state.message ?: stringResource(R.string.plugin_state_setup_required),
|
||||
icon = Icons.Rounded.Info,
|
||||
primaryAction = {
|
||||
TextButton(onClick = {
|
||||
@@ -311,7 +324,7 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
} else if (state is PluginState.Error) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = "This plugin isn't working correctly",
|
||||
text = stringResource(R.string.plugin_state_error),
|
||||
icon = Icons.Rounded.Error,
|
||||
color = MaterialTheme.colorScheme.errorContainer,
|
||||
)
|
||||
@@ -334,6 +347,58 @@ fun PluginSettingsScreen(pluginId: String) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (weatherPlugins.isNotEmpty()) {
|
||||
PreferenceCategory(
|
||||
stringResource(R.string.plugin_type_weather),
|
||||
iconPadding = false,
|
||||
) {
|
||||
for (plugin in weatherPlugins) {
|
||||
val state = plugin.state
|
||||
if (state is PluginState.SetupRequired) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = state.message ?: stringResource(R.string.plugin_state_setup_required),
|
||||
icon = Icons.Rounded.Info,
|
||||
primaryAction = {
|
||||
TextButton(onClick = {
|
||||
try {
|
||||
state.setupActivity.send()
|
||||
} catch (e: PendingIntent.CanceledException) {
|
||||
CrashReporter.logException(e)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.plugin_action_setup))
|
||||
}
|
||||
}
|
||||
)
|
||||
} else if (state is PluginState.Error) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.plugin_state_error),
|
||||
icon = Icons.Rounded.Error,
|
||||
color = MaterialTheme.colorScheme.errorContainer,
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
title = plugin.plugin.label,
|
||||
enabled = state is PluginState.Ready && weatherProviderId != plugin.plugin.authority,
|
||||
iconPadding = false,
|
||||
summary = if (weatherProviderId != plugin.plugin.authority) {
|
||||
stringResource(R.string.plugin_weather_provider_enable)
|
||||
} else {
|
||||
stringResource(R.string.plugin_weather_provider_enabled)
|
||||
}
|
||||
)
|
||||
}
|
||||
Preference(
|
||||
title = stringResource(R.string.widget_config_weather_integration_settings),
|
||||
icon = Icons.AutoMirrored.Rounded.OpenInNew,
|
||||
onClick = {
|
||||
navController?.navigate("settings/integrations/weather")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -7,13 +7,14 @@ import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.files.settings.FileSearchSettings
|
||||
import de.mm20.launcher2.ktx.tryStartActivity
|
||||
import de.mm20.launcher2.plugin.PluginPackage
|
||||
import de.mm20.launcher2.plugin.PluginState
|
||||
import de.mm20.launcher2.plugin.PluginType
|
||||
import de.mm20.launcher2.plugins.PluginService
|
||||
import de.mm20.launcher2.plugins.PluginWithState
|
||||
import de.mm20.launcher2.preferences.search.FileSearchSettings
|
||||
import de.mm20.launcher2.preferences.weather.WeatherSettings
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -30,6 +31,7 @@ import org.koin.core.component.inject
|
||||
class PluginSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val pluginService by inject<PluginService>()
|
||||
private val fileSearchSettings: FileSearchSettings by inject()
|
||||
private val weatherSettings: WeatherSettings by inject()
|
||||
|
||||
private var pluginPackageName = MutableStateFlow<String?>(null)
|
||||
|
||||
@@ -70,6 +72,11 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
it.filter { it.plugin.type == PluginType.FileSearch }
|
||||
}
|
||||
|
||||
val weatherPlugins = states
|
||||
.map {
|
||||
it.filter { it.plugin.type == PluginType.Weather }
|
||||
}
|
||||
|
||||
|
||||
fun init(pluginId: String) {
|
||||
this.pluginPackageName.value = pluginId
|
||||
@@ -101,4 +108,9 @@ class PluginSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
fun setFileSearchPluginEnabled(authority: String, enabled: Boolean) {
|
||||
fileSearchSettings.setPluginEnabled(authority, enabled)
|
||||
}
|
||||
|
||||
val weatherProvider = weatherSettings.providerId
|
||||
fun setWeatherProvider(providerId: String) {
|
||||
weatherSettings.setProvider(providerId)
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,8 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.SearchResultOrder
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.*
|
||||
@@ -232,8 +233,8 @@ fun SearchSettingsScreen() {
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_search_result_ordering),
|
||||
items = listOf(
|
||||
stringResource(R.string.preference_search_result_ordering_alphabetic) to Settings.SearchResultOrderingSettings.Ordering.Alphabetic,
|
||||
stringResource(R.string.preference_search_result_ordering_weighted) to Settings.SearchResultOrderingSettings.Ordering.Weighted
|
||||
stringResource(R.string.preference_search_result_ordering_alphabetic) to SearchResultOrder.Alphabetical,
|
||||
stringResource(R.string.preference_search_result_ordering_weighted) to SearchResultOrder.Weighted
|
||||
),
|
||||
value = searchResultOrdering,
|
||||
onValueChanged = {
|
||||
|
||||
+52
-101
@@ -5,10 +5,16 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.SearchResultOrder
|
||||
import de.mm20.launcher2.preferences.search.CalculatorSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.CalendarSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.ContactSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.UnitConverterSettings
|
||||
import de.mm20.launcher2.preferences.search.WebsiteSearchSettings
|
||||
import de.mm20.launcher2.preferences.search.WikipediaSearchSettings
|
||||
import de.mm20.launcher2.preferences.ui.SearchUiSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -16,35 +22,32 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val searchUiSettings: SearchUiSettings by inject()
|
||||
private val contactSearchSettings: ContactSearchSettings by inject()
|
||||
private val calendarSearchSettings: CalendarSearchSettings by inject()
|
||||
private val shortcutSearchSettings: ShortcutSearchSettings by inject()
|
||||
private val wikipediaSearchSettings: WikipediaSearchSettings by inject()
|
||||
private val websiteSearchSettings: WebsiteSearchSettings by inject()
|
||||
private val unitConverterSettings: UnitConverterSettings by inject()
|
||||
private val calculatorSearchSettings: CalculatorSearchSettings by inject()
|
||||
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
|
||||
val favorites = dataStore.data.map { it.favorites.enabled }
|
||||
val favorites = searchUiSettings.favorites
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setFavorites(favorites: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setFavorites(
|
||||
it.favorites.toBuilder().setEnabled(favorites)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
searchUiSettings.setFavorites(favorites)
|
||||
}
|
||||
|
||||
|
||||
val hasContactsPermission = permissionsManager.hasPermission(PermissionGroup.Contacts)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val contacts = dataStore.data.map { it.contactsSearch.enabled }
|
||||
val contacts = contactSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setContacts(contacts: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setContactsSearch(
|
||||
it.contactsSearch.toBuilder().setEnabled(contacts)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
contactSearchSettings.setEnabled(contacts)
|
||||
}
|
||||
|
||||
fun requestContactsPermission(activity: AppCompatActivity) {
|
||||
@@ -53,133 +56,81 @@ class SearchSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
|
||||
val hasCalendarPermission = permissionsManager.hasPermission(PermissionGroup.Calendar)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val calendar = dataStore.data.map { it.calendarSearch.enabled }
|
||||
val calendar = calendarSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setCalendar(calendar: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setCalendarSearch(
|
||||
it.calendarSearch.toBuilder().setEnabled(calendar)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
calendarSearchSettings.setEnabled(calendar)
|
||||
}
|
||||
|
||||
fun requestCalendarPermission(activity: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(activity, PermissionGroup.Calendar)
|
||||
}
|
||||
|
||||
val calculator = dataStore.data.map { it.calculatorSearch.enabled }
|
||||
val calculator = calculatorSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setCalculator(calculator: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setCalculatorSearch(
|
||||
it.calculatorSearch.toBuilder().setEnabled(calculator)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
calculatorSearchSettings.setEnabled(calculator)
|
||||
}
|
||||
|
||||
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }
|
||||
val unitConverter = unitConverterSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setUnitConverter(unitConverter: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setUnitConverterSearch(
|
||||
it.unitConverterSearch.toBuilder().setEnabled(unitConverter)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
unitConverterSettings.setEnabled(unitConverter)
|
||||
}
|
||||
|
||||
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }
|
||||
val wikipedia = wikipediaSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setWikipedia(wikipedia: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder().setEnabled(wikipedia)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
wikipediaSearchSettings.setEnabled(wikipedia)
|
||||
}
|
||||
|
||||
val websites = dataStore.data.map { it.websiteSearch.enabled }
|
||||
val websites = websiteSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setWebsites(websites: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setWebsiteSearch(
|
||||
it.websiteSearch.toBuilder().setEnabled(websites)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
websiteSearchSettings.setEnabled(websites)
|
||||
}
|
||||
|
||||
val autoFocus = dataStore.data.map { it.searchBar.autoFocus }
|
||||
val autoFocus = searchUiSettings.openKeyboard
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setAutoFocus(autoFocus: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setSearchBar(
|
||||
it.searchBar.toBuilder().setAutoFocus(autoFocus)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
searchUiSettings.setOpenKeyboard(autoFocus)
|
||||
}
|
||||
|
||||
val launchOnEnter = dataStore.data.map { it.searchBar.launchOnEnter }
|
||||
val launchOnEnter = searchUiSettings.launchOnEnter
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setLaunchOnEnter(launchOnEnter: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setSearchBar(
|
||||
it.searchBar.toBuilder().setLaunchOnEnter(launchOnEnter)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
searchUiSettings.setLaunchOnEnter(launchOnEnter)
|
||||
}
|
||||
|
||||
val hasAppShortcutPermission = permissionsManager.hasPermission(PermissionGroup.AppShortcuts)
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
val appShortcuts = dataStore.data.map { it.appShortcutSearch.enabled }
|
||||
val appShortcuts = shortcutSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setAppShortcuts(appShortcuts: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setAppShortcutSearch(
|
||||
it.appShortcutSearch.toBuilder().setEnabled(appShortcuts)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
shortcutSearchSettings.setEnabled(appShortcuts)
|
||||
}
|
||||
|
||||
val searchResultOrdering = dataStore.data.map { it.resultOrdering.ordering }
|
||||
val searchResultOrdering = searchUiSettings.resultOrder
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setSearchResultOrdering(searchResultOrdering: Settings.SearchResultOrderingSettings.Ordering) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setResultOrdering(
|
||||
it.resultOrdering.toBuilder().setOrdering(searchResultOrdering)
|
||||
).build()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSearchResultOrdering(searchResultOrdering: SearchResultOrder) {
|
||||
searchUiSettings.setResultOrder(searchResultOrdering)
|
||||
}
|
||||
|
||||
|
||||
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }
|
||||
val reverseSearchResults = searchUiSettings.reversedResults
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
fun setReverseSearchResults(reverseSearchResults: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setLayout(it.layout.toBuilder().setReverseSearchResults(reverseSearchResults))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
searchUiSettings.setReversedResults(reverseSearchResults)
|
||||
}
|
||||
|
||||
fun requestAppShortcutsPermission(activity: AppCompatActivity) {
|
||||
|
||||
@@ -55,7 +55,7 @@ class EditTagSheetVM : ViewModel(), KoinComponent {
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
allTags = tagService.getAllTags().first().toSet()
|
||||
val items = if (tag != null) tagService.getTaggedItems(tag).first() else emptyList()
|
||||
val apps = appRepository.findMany().first().sorted()
|
||||
val apps = appRepository.findMany().first { it.isNotEmpty() }.sorted()
|
||||
taggedItems = items
|
||||
taggableApps = apps.map { app -> TaggableItem(app, items.any { app.key == it.key }) }
|
||||
taggableOther = items.mapNotNull { item ->
|
||||
|
||||
+6
-24
@@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.unitconverter
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.search.UnitConverterSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -12,35 +12,17 @@ import org.koin.core.component.inject
|
||||
|
||||
class UnitConverterSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val settings: UnitConverterSettings by inject()
|
||||
|
||||
val unitConverter = dataStore.data.map { it.unitConverterSearch.enabled }
|
||||
val unitConverter = settings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setUnitConverter(unitConverter: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setUnitConverterSearch(
|
||||
it.unitConverterSearch.toBuilder()
|
||||
.setEnabled(unitConverter)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
settings.setEnabled(unitConverter)
|
||||
}
|
||||
|
||||
val currencyConverter = dataStore.data.map { it.unitConverterSearch.currencies }
|
||||
val currencyConverter = settings.currencies
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setCurrencyConverter(currencyConverter: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setUnitConverterSearch(
|
||||
it.unitConverterSearch.toBuilder()
|
||||
.setCurrencies(currencyConverter)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
settings.setCurrencies(currencyConverter)
|
||||
}
|
||||
}
|
||||
+33
-1
@@ -1,21 +1,29 @@
|
||||
package de.mm20.launcher2.ui.settings.weather
|
||||
|
||||
import android.app.PendingIntent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Info
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.plugin.PluginState
|
||||
import de.mm20.launcher2.ui.BuildConfig
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.common.WeatherLocationSearchDialog
|
||||
import de.mm20.launcher2.ui.component.Banner
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.*
|
||||
import de.mm20.launcher2.weather.WeatherLocation
|
||||
import de.mm20.launcher2.weather.WeatherProviderInfo
|
||||
|
||||
@Composable
|
||||
@@ -25,12 +33,36 @@ fun WeatherIntegrationSettingsScreen() {
|
||||
|
||||
val availableProviders by viewModel.availableProviders.collectAsState(emptyList())
|
||||
|
||||
val pluginState by viewModel.weatherProviderPluginState.collectAsStateWithLifecycle(
|
||||
null,
|
||||
minActiveState = Lifecycle.State.RESUMED
|
||||
)
|
||||
|
||||
PreferenceScreen(
|
||||
title = stringResource(R.string.preference_screen_weatherwidget),
|
||||
helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/integrations/weather"
|
||||
) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
val state = pluginState?.state
|
||||
if (state is PluginState.SetupRequired) {
|
||||
Banner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = state.message ?: stringResource(R.string.plugin_state_setup_required),
|
||||
icon = Icons.Rounded.Info,
|
||||
primaryAction = {
|
||||
TextButton(onClick = {
|
||||
try {
|
||||
state.setupActivity.send()
|
||||
} catch (e: PendingIntent.CanceledException) {
|
||||
CrashReporter.logException(e)
|
||||
}
|
||||
}) {
|
||||
Text(stringResource(R.string.plugin_action_setup))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val weatherProvider by viewModel.weatherProvider.collectAsState()
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_weather_provider),
|
||||
|
||||
+10
-17
@@ -1,20 +1,15 @@
|
||||
package de.mm20.launcher2.ui.settings.weather
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.weather.WeatherLocation
|
||||
import de.mm20.launcher2.weather.WeatherProviderInfo
|
||||
import de.mm20.launcher2.plugins.PluginService
|
||||
import de.mm20.launcher2.preferences.weather.WeatherSettings
|
||||
import de.mm20.launcher2.weather.WeatherRepository
|
||||
import de.mm20.launcher2.weather.settings.WeatherSettings
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -25,26 +20,24 @@ import org.koin.core.component.inject
|
||||
class WeatherIntegrationSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val repository: WeatherRepository by inject()
|
||||
private val weatherSettings: WeatherSettings by inject()
|
||||
private val pluginService: PluginService by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
val availableProviders = repository.getProviders()
|
||||
|
||||
val weatherProvider = weatherSettings.providerId
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setWeatherProvider(provider: String) {
|
||||
weatherSettings.setProviderId(provider)
|
||||
weatherSettings.setProvider(provider)
|
||||
}
|
||||
|
||||
val imperialUnits = dataStore.data.map { it.weather.imperialUnits }
|
||||
val weatherProviderPluginState = weatherProvider.flatMapLatest {
|
||||
it?.let { pluginService.getPluginWithState(it) } ?: flowOf(null)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
val imperialUnits = weatherSettings.imperialUnits
|
||||
fun setImperialUnits(imperialUnits: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWeather(it.weather.toBuilder().setImperialUnits(imperialUnits))
|
||||
.build()
|
||||
}
|
||||
}
|
||||
weatherSettings.setImperialUnits(imperialUnits)
|
||||
}
|
||||
|
||||
val autoLocation = weatherSettings.autoLocation
|
||||
|
||||
+1
-11
@@ -24,20 +24,10 @@ fun WikipediaSettingsScreen() {
|
||||
viewModel.setWikipedia(it)
|
||||
}
|
||||
)
|
||||
val images by viewModel.images.collectAsState()
|
||||
SwitchPreference(
|
||||
title = stringResource(R.string.preference_search_wikipedia_pictures),
|
||||
summary = stringResource(R.string.preference_search_wikipedia_pictures_summary),
|
||||
enabled = wikipedia == true,
|
||||
value = images == true,
|
||||
onValueChanged = {
|
||||
viewModel.setImages(it)
|
||||
}
|
||||
)
|
||||
val customUrl by viewModel.customUrl.collectAsState()
|
||||
TextPreference(
|
||||
title = stringResource(R.string.preference_wikipedia_customurl),
|
||||
value = customUrl,
|
||||
value = customUrl ?: "",
|
||||
placeholder = stringResource(id = R.string.wikipedia_url),
|
||||
summary = customUrl.takeIf { !it.isNullOrBlank() }
|
||||
?: stringResource(id = R.string.wikipedia_url),
|
||||
|
||||
+6
-39
@@ -2,7 +2,7 @@ package de.mm20.launcher2.ui.settings.wikipedia
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.search.WikipediaSearchSettings
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -11,50 +11,17 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class WikipediaSettingsScreenVM: ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val wikipediaSearchSettings: WikipediaSearchSettings by inject()
|
||||
|
||||
val wikipedia = dataStore.data.map { it.wikipediaSearch.enabled }
|
||||
val wikipedia = wikipediaSearchSettings.enabled
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setWikipedia(wikipedia: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setEnabled(wikipedia)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
wikipediaSearchSettings.setEnabled(wikipedia)
|
||||
}
|
||||
|
||||
val images = dataStore.data.map { it.wikipediaSearch.images }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||
fun setImages(images: Boolean) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setImages(images)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val customUrl = dataStore.data.map { it.wikipediaSearch.customUrl }
|
||||
val customUrl = wikipediaSearchSettings.customUrl
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), "")
|
||||
fun setCustomUrl(customUrl: String) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.setWikipediaSearch(
|
||||
it.wikipediaSearch.toBuilder()
|
||||
.setCustomUrl(customUrl)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
wikipediaSearchSettings.setCustomUrl(customUrl)
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,18 @@
|
||||
package de.mm20.launcher2.ui.theme
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.shape.CornerSize
|
||||
import androidx.compose.foundation.shape.CutCornerShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.coerceAtMost
|
||||
import androidx.compose.ui.unit.dp
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.AppearanceSettings
|
||||
import de.mm20.launcher2.themes.DefaultThemeId
|
||||
import de.mm20.launcher2.themes.Theme
|
||||
import de.mm20.launcher2.preferences.Font
|
||||
import de.mm20.launcher2.preferences.SurfaceShape
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.ThemeRepository
|
||||
import de.mm20.launcher2.ui.locals.LocalDarkTheme
|
||||
import de.mm20.launcher2.ui.theme.colorscheme.*
|
||||
@@ -26,7 +21,7 @@ import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.androidx.compose.inject
|
||||
import java.util.UUID
|
||||
import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref
|
||||
|
||||
|
||||
@Composable
|
||||
@@ -35,33 +30,31 @@ fun LauncherTheme(
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val dataStore: LauncherDataStore by inject()
|
||||
val uiSettings: UiSettings by inject()
|
||||
val themeRepository: ThemeRepository by inject()
|
||||
|
||||
val theme by remember {
|
||||
dataStore.data.map {
|
||||
it.appearance.themeId.takeIf { it.isNotEmpty() }?.let {
|
||||
UUID.fromString(it)
|
||||
}
|
||||
}.flatMapLatest {
|
||||
uiSettings.theme.flatMapLatest {
|
||||
themeRepository.getThemeOrDefault(it)
|
||||
}
|
||||
}.collectAsState(themeRepository.getDefaultTheme())
|
||||
|
||||
val themePreference by remember { dataStore.data.map { it.appearance.theme } }.collectAsState(
|
||||
AppearanceSettings.Theme.System
|
||||
val colorSchemePref by remember { uiSettings.colorScheme }.collectAsState(
|
||||
ColorSchemePref.System
|
||||
)
|
||||
val darkTheme =
|
||||
themePreference == AppearanceSettings.Theme.Dark || themePreference == AppearanceSettings.Theme.System && isSystemInDarkTheme()
|
||||
colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme()
|
||||
|
||||
val cornerRadius by remember {
|
||||
dataStore.data.map { it.cards.radius.dp }
|
||||
uiSettings.cardStyle.map {
|
||||
it.cornerRadius.dp
|
||||
}
|
||||
}.collectAsState(8.dp)
|
||||
|
||||
val baseShape by remember {
|
||||
dataStore.data.map {
|
||||
when (it.cards.shape) {
|
||||
Settings.CardSettings.Shape.Cut -> CutCornerShape(0f)
|
||||
uiSettings.cardStyle.map {
|
||||
when (it.shape) {
|
||||
SurfaceShape.Cut -> CutCornerShape(0f)
|
||||
else -> RoundedCornerShape(0f)
|
||||
}
|
||||
}
|
||||
@@ -73,8 +66,8 @@ fun LauncherTheme(
|
||||
lightColorSchemeOf(theme)
|
||||
}
|
||||
|
||||
val font by remember { dataStore.data.map { it.appearance.font } }.collectAsState(
|
||||
AppearanceSettings.Font.Outfit
|
||||
val font by remember { uiSettings.font }.collectAsState(
|
||||
Font.Outfit
|
||||
)
|
||||
|
||||
val typography = remember(font) {
|
||||
@@ -99,9 +92,9 @@ fun LauncherTheme(
|
||||
}
|
||||
}
|
||||
|
||||
fun getTypography(context: Context, font: AppearanceSettings.Font?): Typography {
|
||||
fun getTypography(context: Context, font: Font?): Typography {
|
||||
return when (font) {
|
||||
AppearanceSettings.Font.SystemDefault -> getDeviceDefaultTypography(context)
|
||||
Font.System -> getDeviceDefaultTypography(context)
|
||||
else -> DefaultTypography
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,8 @@ import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.datastore.core.DataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.LegacySettings
|
||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||
import de.mm20.launcher2.themes.CorePalette
|
||||
import de.mm20.launcher2.themes.DefaultDarkColorScheme
|
||||
import de.mm20.launcher2.themes.DefaultLightColorScheme
|
||||
@@ -23,6 +24,7 @@ import de.mm20.launcher2.themes.merge
|
||||
import de.mm20.launcher2.ui.locals.LocalWallpaperColors
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koin.androidx.compose.inject
|
||||
import org.koin.core.component.inject
|
||||
|
||||
@Composable
|
||||
fun lightColorSchemeOf(theme: Theme): ColorScheme {
|
||||
@@ -82,12 +84,12 @@ fun colorSchemeOf(colorScheme: FullColorScheme, corePalette: PartialCorePalette)
|
||||
|
||||
@Composable
|
||||
fun systemCorePalette(): CorePalette<Int> {
|
||||
val dataStore: DataStore<Settings> by inject()
|
||||
val compatMode by remember {
|
||||
dataStore.data.map { it.appearance.forceCompatModeSystemColors }
|
||||
val uiSettings: UiSettings by inject()
|
||||
val compatModeColors by remember {
|
||||
uiSettings.compatModeColors
|
||||
}.collectAsState(false)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31 && !compatMode) {
|
||||
if (Build.VERSION.SDK_INT >= 31 && !compatModeColors) {
|
||||
val context = LocalContext.current
|
||||
return CorePalette(
|
||||
primary = ContextCompat.getColor(context, android.R.color.system_accent1_500),
|
||||
@@ -112,7 +114,7 @@ fun systemCorePalette(): CorePalette<Int> {
|
||||
}
|
||||
}
|
||||
|
||||
fun CustomColorScheme(colors: Settings.AppearanceSettings.CustomColors.Scheme): ColorScheme {
|
||||
fun CustomColorScheme(colors: LegacySettings.AppearanceSettings.CustomColors.Scheme): ColorScheme {
|
||||
return ColorScheme(
|
||||
primary = Color(colors.primary),
|
||||
onPrimary = Color(colors.onPrimary),
|
||||
|
||||
Reference in New Issue
Block a user