diff --git a/preferences/src/main/proto/settings.proto b/preferences/src/main/proto/settings.proto index 8bce802b..135ce0df 100644 --- a/preferences/src/main/proto/settings.proto +++ b/preferences/src/main/proto/settings.proto @@ -64,6 +64,12 @@ message Settings { } CustomColors custom_colors = 8; bool dim_wallpaper = 7; + + enum Layout { + PullDown = 0; + Pager = 1; + } + Layout layout = 9; } AppearanceSettings appearance = 2; diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt index 8b4a0f61..26f13b4d 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt @@ -29,6 +29,7 @@ import com.afollestad.materialdialogs.customview.customView import com.android.launcher3.GestureNavContract import com.google.accompanist.systemuicontroller.rememberSystemUiController import de.mm20.launcher2.icons.DynamicIconController +import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.base.BaseActivity import de.mm20.launcher2.ui.base.ProvideSettings @@ -71,6 +72,7 @@ class LauncherActivity : BaseActivity() { val hideStatus by viewModel.hideStatusBar.observeAsState(false) val hideNav by viewModel.hideNavBar.observeAsState(false) val dimBackground by viewModel.dimBackground.observeAsState(false) + val layout by viewModel.layout.observeAsState(null) val systemUiController = rememberSystemUiController() @@ -88,13 +90,27 @@ class LauncherActivity : BaseActivity() { contentAlignment = Alignment.BottomCenter ) { NavBarEffects(modifier = Modifier.fillMaxSize()) - PagerScaffold( - modifier = Modifier - .fillMaxSize() - .systemBarsPadding(), - darkStatusBarIcons = lightStatus, - darkNavBarIcons = lightNav, - ) + when(layout) { + Settings.AppearanceSettings.Layout.PullDown -> { + PullDownScaffold( + modifier = Modifier + .fillMaxSize() + .systemBarsPadding(), + darkStatusBarIcons = lightStatus, + darkNavBarIcons = lightNav, + ) + } + Settings.AppearanceSettings.Layout.Pager -> { + PagerScaffold( + modifier = Modifier + .fillMaxSize() + .systemBarsPadding(), + darkStatusBarIcons = lightStatus, + darkNavBarIcons = lightNav, + ) + } + else -> {} + } } val showHiddenItems by viewModel.isHiddenItemsShown.observeAsState(false) if (showHiddenItems) { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivityVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivityVM.kt index e6a31673..2dc1e5a8 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivityVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivityVM.kt @@ -63,4 +63,6 @@ class LauncherActivityVM : ViewModel(), KoinComponent { fun hideHiddenItems() { isHiddenItemsShown.value = false } + + val layout = dataStore.data.map { it.appearance.layout }.asLiveData() } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index 09644d7e..70db5bee 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -150,9 +150,6 @@ fun PagerScaffold( SearchColumn( modifier = Modifier .fillMaxSize() - .onSizeChanged { - size = it - } .verticalScroll(searchScrollState, reverseScrolling = true) .imePadding() .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) @@ -174,6 +171,9 @@ fun PagerScaffold( modifier = Modifier .fillMaxSize() + .onSizeChanged { + size = it + } .verticalScroll(widgetsScrollState) .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) .padding(top = editModePadding), diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt index b38398ec..ff3ec735 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetColumn.kt @@ -39,6 +39,7 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.ui.ClockWidget import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.ktx.animateTo import de.mm20.launcher2.ui.launcher.widgets.picker.PickAppWidgetActivity import de.mm20.launcher2.widgets.ExternalWidget import kotlinx.coroutines.awaitCancellation @@ -106,10 +107,11 @@ fun WidgetColumn( val swapThresholds = remember(widgets) { Array(widgets.size) { floatArrayOf(0f, 0f) } } - for ((i, widget) in widgets.withIndex()) { + val widgetsWithIndex = remember(widgets) { widgets.withIndex() } + for ((i, widget) in widgetsWithIndex) { key(if (widget is ExternalWidget) widget.widgetId else widget) { var dragOffsetAfterSwap = remember { null } - val offsetY = remember(widgets) { Animatable(dragOffsetAfterSwap ?: 0f) } + val offsetY = remember(widgets) { mutableStateOf(dragOffsetAfterSwap ?: 0f) } LaunchedEffect(widgets) { dragOffsetAfterSwap = null @@ -141,7 +143,7 @@ fun WidgetColumn( draggableState = rememberDraggableState { scope.launch { val newOffset = offsetY.value + it - offsetY.snapTo(newOffset) + offsetY.value = newOffset if (i > 0 && newOffset < (swapThresholds[i - 1][0] - swapThresholds[i - 1][1])) { if (dragOffsetAfterSwap == null) { dragOffsetAfterSwap = diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index 7bafecf6..c9fa869b 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -29,10 +29,9 @@ import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.HorizontalPagerIndicator import com.google.accompanist.pager.rememberPagerState import de.mm20.launcher2.icons.LauncherIcon +import de.mm20.launcher2.preferences.Settings.* import de.mm20.launcher2.preferences.Settings.AppearanceSettings.ColorScheme import de.mm20.launcher2.preferences.Settings.AppearanceSettings.Theme -import de.mm20.launcher2.preferences.Settings.IconSettings -import de.mm20.launcher2.preferences.Settings.SearchBarSettings import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.ShapedLauncherIcon import de.mm20.launcher2.ui.component.getShape @@ -78,6 +77,10 @@ fun AppearanceSettingsScreen() { navController?.navigate("settings/appearance/colorscheme") } ) + val layout by viewModel.layout.observeAsState() + LayoutPreference(title = "Layout", value = layout, onValueChanged = { + viewModel.setLayout(it) + }) Preference( title = stringResource(R.string.preference_cards), summary = stringResource(R.string.preference_cards_summary), @@ -465,6 +468,77 @@ fun LegacyIconBackgroundPreference( } } +@OptIn(ExperimentalPagerApi::class) +@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 + .height(150.dp) + .padding(bottom = 16.dp) + .background(MaterialTheme.colorScheme.secondary) + ) { + when (layouts[it]) { + AppearanceSettings.Layout.PullDown -> PullDownLayoutPreview() + AppearanceSettings.Layout.Pager -> PagerLayoutPreview() + else -> {} + } + } + HorizontalPagerIndicator(pagerState = pagerState) + } + } + ) + } +} + +@Composable +fun PullDownLayoutPreview() { +} + + +@Composable +fun PagerLayoutPreview() { +} + @Composable private fun getShapeName(shape: IconSettings.IconShape?): String? { diff --git a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index e350235f..01e76834 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -217,4 +217,18 @@ 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() + } + } + } } \ No newline at end of file