Add preferences for layout

This commit is contained in:
MM20 2023-01-16 17:49:45 +01:00
parent 7cd1269c33
commit 64cc07bef9
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
14 changed files with 273 additions and 218 deletions

View File

@ -37,6 +37,8 @@ fun AssistantScaffold(
modifier: Modifier = Modifier,
darkStatusBarIcons: Boolean = false,
darkNavBarIcons: Boolean = false,
bottomSearchBar: Boolean = false,
reverseSearchResults: Boolean = false,
) {
val viewModel: LauncherScaffoldVM = viewModel()
@ -54,15 +56,6 @@ fun AssistantScaffold(
}
}
val bottomSearchBar by remember {
viewModel.dataStore.data.map { it.appearance.layout != Settings.AppearanceSettings.Layout.PullDown }
}.collectAsState(null)
val reverseResults by remember {
viewModel.dataStore.data.map { it.appearance.layout != Settings.AppearanceSettings.Layout.PullDown }
}.collectAsState(null)
val searchState = rememberLazyListState()
val isSearchAtStart by remember {
@ -79,14 +72,11 @@ fun AssistantScaffold(
}
}
if (reverseResults == null || bottomSearchBar == null) return
val searchBarLevel by remember {
derivedStateOf {
when {
reverseResults == bottomSearchBar && isSearchAtStart -> SearchBarLevel.Active
reverseResults != bottomSearchBar && isSearchAtEnd -> SearchBarLevel.Active
reverseSearchResults == bottomSearchBar && isSearchAtStart -> SearchBarLevel.Active
reverseSearchResults != bottomSearchBar && isSearchAtEnd -> SearchBarLevel.Active
else -> SearchBarLevel.Raised
}
}
@ -95,7 +85,7 @@ fun AssistantScaffold(
val systemUiController = rememberSystemUiController()
val showStatusBarScrim by remember {
derivedStateOf {
if (reverseResults == true) {
if (reverseSearchResults) {
!isSearchAtEnd
} else {
!isSearchAtStart
@ -104,7 +94,7 @@ fun AssistantScaffold(
}
val showNavBarScrim by remember {
derivedStateOf {
if (reverseResults == true) {
if (reverseSearchResults) {
!isSearchAtStart
} else {
!isSearchAtEnd
@ -154,7 +144,7 @@ fun AssistantScaffold(
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val y = available.y * if (reverseResults == true) -1f else 1f
val y = available.y * if (reverseSearchResults) -1f else 1f
searchBarOffset = (searchBarOffset + y).coerceIn(-maxSearchBarOffset, 0f)
return super.onPreScroll(available, source)
}
@ -175,10 +165,10 @@ fun AssistantScaffold(
SearchColumn(
modifier = Modifier.fillMaxSize(),
paddingValues = PaddingValues(
top = (if (bottomSearchBar == true) 0.dp else 56.dp + webSearchPadding) + 4.dp + windowInsets.calculateTopPadding(),
bottom = (if (bottomSearchBar == true) 56.dp + webSearchPadding else 0.dp) + 4.dp + windowInsets.calculateBottomPadding()
top = (if (bottomSearchBar) 0.dp else 56.dp + webSearchPadding) + 4.dp + windowInsets.calculateTopPadding(),
bottom = (if (bottomSearchBar) 56.dp + webSearchPadding else 0.dp) + 4.dp + windowInsets.calculateBottomPadding()
),
reverse = reverseResults == true,
reverse = reverseSearchResults,
state = searchState
)
@ -191,14 +181,14 @@ fun AssistantScaffold(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.align(if (bottomSearchBar == true) Alignment.BottomCenter else Alignment.TopCenter)
.align(if (bottomSearchBar) Alignment.BottomCenter else Alignment.TopCenter)
.windowInsetsPadding(WindowInsets.safeDrawing)
.padding(8.dp)
.offset {
if (searchBarFocused) IntOffset.Zero
else IntOffset(
0,
searchBarOffset.toInt() * if (bottomSearchBar == true) -1 else 1
searchBarOffset.toInt() * if (bottomSearchBar) -1 else 1
)
},
level = { searchBarLevel },
@ -213,7 +203,7 @@ fun AssistantScaffold(
onValueChange = { searchVM.search(it) },
darkColors = LocalPreferDarkContentOverWallpaper.current && searchBarColor == Settings.SearchBarSettings.SearchBarColors.Auto || searchBarColor == Settings.SearchBarSettings.SearchBarColors.Dark,
style = searchBarStyle,
reverse = bottomSearchBar == true
reverse = bottomSearchBar
)
}
}

View File

@ -37,5 +37,7 @@ class LauncherActivityVM : ViewModel(), KoinComponent {
isSystemInDarkMode.value = darkMode
}
val layout = dataStore.data.map { it.appearance.layout }.asLiveData()
val baseLayout = dataStore.data.map { it.layout.baseLayout }.asLiveData()
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }.asLiveData()
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }.asLiveData()
}

View File

@ -1,6 +1,5 @@
package de.mm20.launcher2.ui.launcher
import android.util.Log
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
@ -117,15 +116,13 @@ fun PagerScaffold(
}
val isSearchAtTop by remember {
if (reverseSearchResults) {
derivedStateOf {
if (reverseSearchResults) {
val lastItem =
searchState.layoutInfo.visibleItemsInfo.lastOrNull()
?: return@derivedStateOf true
lastItem.offset + lastItem.size <= searchState.layoutInfo.viewportEndOffset - searchState.layoutInfo.afterContentPadding
}
} else {
derivedStateOf {
searchState.firstVisibleItemIndex == 0 && searchState.firstVisibleItemScrollOffset == 0
}
}
@ -255,8 +252,10 @@ fun PagerScaffold(
source: NestedScrollSource
): Offset {
if (source == NestedScrollSource.Drag) gestureManager.dispatchDrag(available)
val deltaSearchBarOffset = consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1
searchBarOffset.value = (searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset)
val deltaSearchBarOffset =
consumed.y * if (isSearchOpen && reverseSearchResults) 1 else -1
searchBarOffset.value =
(searchBarOffset.value + deltaSearchBarOffset).coerceIn(0f, maxSearchBarOffset)
return super.onPostScroll(consumed, available, source)
}
@ -311,8 +310,9 @@ fun PagerScaffold(
state = pagerState,
userScrollEnabled = !isWidgetEditMode,
) {
val pagerProgress = pagerState.currentPage + pagerState.currentPageOffsetFraction
when(it) {
val pagerProgress =
pagerState.currentPage + pagerState.currentPageOffsetFraction
when (it) {
0 -> {
val editModePadding by animateDpAsState(if (isWidgetEditMode && bottomSearchBar) 56.dp else 0.dp)
@ -382,6 +382,7 @@ fun PagerScaffold(
)
}
}
1 -> {
val webSearchPadding by animateDpAsState(
if (actions.isEmpty()) 0.dp else 48.dp
@ -469,7 +470,12 @@ fun PagerScaffold(
.padding(8.dp)
.windowInsetsPadding(WindowInsets.safeDrawing)
.imePadding()
.offset { IntOffset(0, if (focusSearchBar) 0 else searchBarOffset.value.toInt() * if (bottomSearchBar) 1 else -1) }
.offset {
IntOffset(
0,
if (focusSearchBar) 0 else searchBarOffset.value.toInt() * if (bottomSearchBar) 1 else -1
)
}
.offset(y = widgetEditModeOffset),
level = { searchBarLevel },
focused = focusSearchBar,

View File

@ -18,6 +18,7 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -117,7 +118,9 @@ abstract class SharedLauncherActivity(
val hideStatus by viewModel.hideStatusBar.observeAsState(false)
val hideNav by viewModel.hideNavBar.observeAsState(false)
val layout by viewModel.layout.observeAsState(null)
val layout by viewModel.baseLayout.observeAsState(null)
val bottomSearchBar by viewModel.bottomSearchBar.observeAsState(false)
val reverseSearchResults by viewModel.reverseSearchResults.observeAsState(false)
val systemUiController = rememberSystemUiController()
@ -167,15 +170,20 @@ abstract class SharedLauncherActivity(
) {
NavBarEffects(modifier = Modifier.fillMaxSize())
if (mode == LauncherActivityMode.Assistant) {
key(bottomSearchBar, reverseSearchResults) {
AssistantScaffold(
modifier = Modifier
.fillMaxSize(),
darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav,
bottomSearchBar = bottomSearchBar,
reverseSearchResults = reverseSearchResults,
)
}
} else {
when (layout) {
Settings.AppearanceSettings.Layout.PullDown -> {
Settings.LayoutSettings.Layout.PullDown -> {
key(bottomSearchBar, reverseSearchResults) {
PullDownScaffold(
modifier = Modifier
.fillMaxSize()
@ -188,11 +196,15 @@ abstract class SharedLauncherActivity(
},
darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav,
bottomSearchBar = bottomSearchBar,
reverseSearchResults = reverseSearchResults,
)
}
}
Settings.AppearanceSettings.Layout.Pager,
Settings.AppearanceSettings.Layout.PagerReversed -> {
Settings.LayoutSettings.Layout.Pager,
Settings.LayoutSettings.Layout.PagerReversed -> {
key(bottomSearchBar, reverseSearchResults) {
PagerScaffold(
modifier = Modifier
.fillMaxSize()
@ -203,9 +215,12 @@ abstract class SharedLauncherActivity(
},
darkStatusBarIcons = lightStatus,
darkNavBarIcons = lightNav,
reverse = layout == Settings.AppearanceSettings.Layout.PagerReversed
reverse = layout == Settings.LayoutSettings.Layout.PagerReversed,
bottomSearchBar = bottomSearchBar,
reverseSearchResults = reverseSearchResults,
)
}
}
else -> {}
}

View File

@ -40,6 +40,7 @@ import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
import de.mm20.launcher2.ui.settings.favorites.FavoritesSettingsScreen
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
import de.mm20.launcher2.ui.settings.hiddenitems.HiddenItemsSettingsScreen
import de.mm20.launcher2.ui.settings.layout.LayoutSettingsScreen
import de.mm20.launcher2.ui.settings.license.LicenseScreen
import de.mm20.launcher2.ui.settings.log.LogScreen
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
@ -99,6 +100,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/appearance") {
AppearanceSettingsScreen()
}
composable("settings/appearance/layout") {
LayoutSettingsScreen()
}
composable("settings/appearance/colorscheme") {
ColorSchemeSettingsScreen()
}

View File

@ -54,13 +54,13 @@ fun AppearanceSettingsScreen() {
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item {
PreferenceCategory {
val layout by viewModel.layout.observeAsState()
LayoutPreference(
Preference(
title = stringResource(id = R.string.preference_layout),
summary = stringResource(id = R.string.preference_layout_summary),
value = layout, onValueChanged = {
viewModel.setLayout(it)
})
onClick = {
navController?.navigate("settings/appearance/layout")
}
)
val theme by viewModel.theme.observeAsState()
ListPreference(
title = stringResource(id = R.string.preference_theme),
@ -483,128 +483,6 @@ fun IconShapePreference(
}
}
@Composable
fun LayoutPreference(
title: String,
summary: String? = null,
value: AppearanceSettings.Layout?,
onValueChanged: (AppearanceSettings.Layout) -> Unit
) {
var showDialog by remember { mutableStateOf(false) }
Preference(title = title, summary = summary, onClick = { showDialog = true })
if (showDialog && value != null) {
val layouts = remember {
AppearanceSettings.Layout.values()
.filter { it != AppearanceSettings.Layout.UNRECOGNIZED }
}
val pagerState = rememberPagerState(layouts.indexOf(value))
AlertDialog(
onDismissRequest = { showDialog = false },
confirmButton = {
TextButton(onClick = {
showDialog = false
onValueChanged(layouts[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(
count = layouts.size,
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp)
) {
Box(
modifier = Modifier
.height(250.dp)
.width(141.dp)
.background(MaterialTheme.colorScheme.secondary)
) {
val composition by rememberLottieComposition(
LottieCompositionSpec.RawRes(
when (layouts[it]) {
AppearanceSettings.Layout.PullDown -> R.raw.lottie_scaffold_pulldown
AppearanceSettings.Layout.Pager -> R.raw.lottie_scaffold_pager
AppearanceSettings.Layout.PagerReversed -> R.raw.lottie_scaffold_pager_reverse
else -> 0
}
)
)
val dynamicProperties = rememberLottieDynamicProperties(
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
value = MaterialTheme.colorScheme.primaryContainer.toArgb(),
keyPath = arrayOf("Pointer", "**")
),
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
value = MaterialTheme.colorScheme.surface.toArgb(),
keyPath = arrayOf("SearchBar", "**")
),
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
value = MaterialTheme.colorScheme.surface.toArgb(),
keyPath = arrayOf("Favorites", "**")
),
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
value = MaterialTheme.colorScheme.surface.toArgb(),
keyPath = arrayOf("Apps", "**")
),
rememberLottieDynamicProperty(
property = LottieProperty.COLOR,
value = Color.White.toArgb(),
keyPath = arrayOf("ClockWidget", "**")
)
)
/*LaunchedEffect(null) {
val drw = LottieDrawable()
drw.composition = composition
val list = drw.resolveKeyPath(KeyPath("**"))
list.forEach {
Log.d("MM20", it.keysToString())
}
}*/
val progress by animateLottieCompositionAsState(
composition,
iterations = LottieConstants.IterateForever
)
LottieAnimation(
composition = composition,
progress = progress,
dynamicProperties = dynamicProperties
)
}
}
HorizontalPagerIndicator(pagerState = pagerState)
}
}
)
}
}
@Composable
private fun getShapeName(shape: IconSettings.IconShape?): String? {

View File

@ -288,18 +288,4 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
}
}
}
val layout = dataStore.data.map { it.appearance.layout }.asLiveData()
fun setLayout(layout: Settings.AppearanceSettings.Layout) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setAppearance(
it.appearance.toBuilder()
.setLayout(layout)
)
.build()
}
}
}
}

View File

@ -0,0 +1,60 @@
package de.mm20.launcher2.ui.settings.layout
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.preferences.ListPreference
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
@Composable
fun LayoutSettingsScreen() {
val viewModel: LayoutSettingsScreenVM = viewModel()
PreferenceScreen(
title = stringResource(id = R.string.preference_layout)
) {
item {
PreferenceCategory {
val baseLayout by viewModel.baseLayout.observeAsState()
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,
),
value = baseLayout,
onValueChanged = {
if (it != null) viewModel.setBaseLayout(it)
},
)
val bottomSearchBar by viewModel.bottomSearchBar.observeAsState()
ListPreference(
title = stringResource(R.string.preference_layout_search_bar_position),
items = listOf(
stringResource(R.string.search_bar_position_top) to false,
stringResource(R.string.search_bar_position_bottom) to true,
),
value = bottomSearchBar,
onValueChanged = {
if (it != null) viewModel.setBottomSearchBar(it)
},
)
val reverseSearchResults by viewModel.reverseSearchResults.observeAsState()
ListPreference(title = stringResource(R.string.preference_layout_search_results),
items = listOf(
stringResource(R.string.search_results_order_top_down) to false,
stringResource(R.string.search_results_order_bottom_up) to true,
),
value = reverseSearchResults,
onValueChanged = {
if (it != null) viewModel.setReverseSearchResults(it)
},
)
}
}
}
}

View File

@ -0,0 +1,49 @@
package de.mm20.launcher2.ui.settings.layout
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class LayoutSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore : LauncherDataStore by inject()
val baseLayout = dataStore.data.map { it.layout.baseLayout }.asLiveData()
fun setBaseLayout(baseLayout: Layout) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setBaseLayout(baseLayout))
.build()
}
}
}
val bottomSearchBar = dataStore.data.map { it.layout.bottomSearchBar }.asLiveData()
fun setBottomSearchBar(bottomSearchBar: Boolean) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setBottomSearchBar(bottomSearchBar))
.build()
}
}
}
val reverseSearchResults = dataStore.data.map { it.layout.reverseSearchResults }.asLiveData()
fun setReverseSearchResults(reverseSearchResults: Boolean) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setLayout(it.layout.toBuilder().setReverseSearchResults(reverseSearchResults))
.build()
}
}
}
}

View File

@ -722,4 +722,14 @@
<item quantity="one">%1$d item selected</item>
<item quantity="other">%1$d items selected</item>
</plurals>
<string name="preference_layout_open_search">Search activation</string>
<string name="open_search_pull_down">Swipe down</string>
<string name="open_search_swipe_left">Swipe left</string>
<string name="open_search_swipe_right">Swipe right</string>
<string name="preference_layout_search_bar_position">Search bar position</string>
<string name="search_bar_position_top">Top</string>
<string name="search_bar_position_bottom">Bottom</string>
<string name="preference_layout_search_results">Arrangement of search results</string>
<string name="search_results_order_top_down">Top-down</string>
<string name="search_results_order_bottom_up">Bottom-up</string>
</resources>

View File

@ -22,7 +22,7 @@ internal val Context.dataStore: LauncherDataStore by dataStore(
}
)
internal const val SchemaVersion = 11
internal const val SchemaVersion = 12
internal fun getMigrations(context: Context): List<DataMigration<Settings>> {
return listOf(
@ -37,5 +37,6 @@ internal fun getMigrations(context: Context): List<DataMigration<Settings>> {
Migration_8_9(),
Migration_9_10(),
Migration_10_11(),
Migration_11_12(),
)
}

View File

@ -162,6 +162,12 @@ fun createFactorySettings(context: Context): Settings {
Settings.WidgetSettings.newBuilder()
.setEditButton(true)
)
.setLayout(
Settings.LayoutSettings.newBuilder()
.setBaseLayout(Settings.LayoutSettings.Layout.PullDown)
.setBottomSearchBar(false)
.setReverseSearchResults(false)
)
.build()
}

View File

@ -0,0 +1,37 @@
package de.mm20.launcher2.preferences.migrations
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.preferences.Settings.LayoutSettings
class Migration_11_12: VersionedMigration(11, 12) {
override suspend fun applyMigrations(builder: Settings.Builder): Settings.Builder {
val oldLayout = builder.appearance.layout
when(oldLayout) {
LayoutSettings.Layout.Pager -> {
builder.setLayout(
LayoutSettings.newBuilder()
.setBaseLayout(LayoutSettings.Layout.Pager)
.setBottomSearchBar(true)
.setReverseSearchResults(true)
)
}
LayoutSettings.Layout.PagerReversed -> {
builder.setLayout(
LayoutSettings.newBuilder()
.setBaseLayout(LayoutSettings.Layout.PagerReversed)
.setBottomSearchBar(true)
.setReverseSearchResults(true)
)
}
else -> {
builder.setLayout(
LayoutSettings.newBuilder()
.setBaseLayout(LayoutSettings.Layout.PullDown)
.setBottomSearchBar(false)
.setReverseSearchResults(false)
)
}
}
return builder
}
}

View File

@ -68,12 +68,10 @@ message Settings {
CustomColors custom_colors = 8;
bool dim_wallpaper = 7;
enum Layout {
PullDown = 0;
Pager = 1;
PagerReversed = 2;
}
Layout layout = 9;
/**
* Deprecated, use layout instead
*/
LayoutSettings.Layout layout = 9 [deprecated = true];
enum Font {
Outfit = 0;
@ -288,4 +286,17 @@ message Settings {
bool edit_button = 1;
}
WidgetSettings widgets = 26;
message LayoutSettings {
enum Layout {
PullDown = 0;
Pager = 1;
PagerReversed = 2;
}
Layout base_layout = 1;
bool bottom_search_bar = 2;
bool reverse_search_results = 3;
}
LayoutSettings layout = 27;
}