From 764aa3e5877fa2a3fbc2a6077726491f66a2e187 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Mon, 16 May 2022 21:24:19 +0200 Subject: [PATCH] Improve pager scaffold performance --- .../launcher2/ui/launcher/PagerScaffold.kt | 158 +++++++++++------- .../launcher2/ui/launcher/PullDownScaffold.kt | 1 - 2 files changed, 100 insertions(+), 59 deletions(-) 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 83696282..207d5195 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 @@ -7,38 +7,46 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.slideIn import androidx.compose.animation.slideOut +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.gestures.LocalOverScrollConfiguration +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.FractionalThreshold import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Done +import androidx.compose.material.rememberSwipeableState +import androidx.compose.material.swipeable import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.pager.ExperimentalPagerApi -import com.google.accompanist.pager.HorizontalPager -import com.google.accompanist.pager.rememberPagerState import com.google.accompanist.systemuicontroller.rememberSystemUiController import de.mm20.launcher2.ktx.isAtLeastApiLevel import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.ktx.toPixels import de.mm20.launcher2.ui.launcher.search.SearchBar import de.mm20.launcher2.ui.launcher.search.SearchBarLevel import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.widgets.WidgetColumn +import kotlin.math.roundToInt -@OptIn(ExperimentalPagerApi::class) +@OptIn( + ExperimentalPagerApi::class, ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class, + ExperimentalFoundationApi::class +) @Composable fun PagerScaffold( modifier: Modifier = Modifier, @@ -52,9 +60,9 @@ fun PagerScaffold( val isSearchOpen by viewModel.isSearchOpen.observeAsState(false) val isWidgetEditMode by viewModel.isWidgetEditMode.observeAsState(false) - val pagerState = rememberPagerState() val widgetsScrollState = rememberScrollState() val searchScrollState = rememberScrollState() + val swipeableState = rememberSwipeableState(if (isSearchOpen) Page.Search else Page.Widgets) val isWidgetsScrollZero by remember { derivedStateOf { @@ -88,8 +96,8 @@ fun PagerScaffold( val blurWallpaper by remember { derivedStateOf { - isSearchOpen || pagerState.currentPage == 0 && pagerState.currentPageOffset > 0.5f || - pagerState.currentPage == 1 && pagerState.currentPageOffset <= 0.5f || + isSearchOpen || swipeableState.progress.to == Page.Widgets && swipeableState.progress.fraction <= 0.5f || + swipeableState.progress.to == Page.Search && swipeableState.progress.fraction > 0.5f || !isWidgetsScrollZero } } @@ -97,6 +105,7 @@ fun PagerScaffold( val density = LocalDensity.current + LaunchedEffect(blurWallpaper) { if (!isAtLeastApiLevel(31)) return@LaunchedEffect (context as Activity).window.attributes = context.window.attributes.also { @@ -110,16 +119,16 @@ fun PagerScaffold( } } - val currentPage = pagerState.currentPage + val currentPage = swipeableState.currentValue LaunchedEffect(currentPage) { - if (currentPage == 1) viewModel.openSearch() + if (currentPage == Page.Search) viewModel.openSearch() else viewModel.closeSearch() } LaunchedEffect(isSearchOpen) { - if (isSearchOpen) pagerState.animateScrollToPage(1) + if (isSearchOpen) swipeableState.animateTo(Page.Search) else { - pagerState.animateScrollToPage(0) + swipeableState.animateTo(Page.Widgets) searchVM.search("") } } @@ -136,64 +145,93 @@ fun PagerScaffold( } } - Box( modifier = modifier ) { - HorizontalPager( - count = 2, - state = pagerState, - modifier = Modifier - .fillMaxSize(), - userScrollEnabled = !isWidgetEditMode + BoxWithConstraints( + modifier = Modifier.fillMaxWidth() ) { - if (it == 1) { - val websearches by searchVM.websearchResults.observeAsState(emptyList()) - val webSearchPadding by animateDpAsState( - if (websearches.isEmpty()) 0.dp else 48.dp - ) - SearchColumn( - modifier = Modifier - .fillMaxSize() - .verticalScroll(searchScrollState, reverseScrolling = true) - .imePadding() - .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) - .padding(bottom = webSearchPadding), - reverse = true, - ) + val height by remember { + derivedStateOf { maxHeight } + } + val width by remember { + derivedStateOf { maxWidth } } - if (it == 0) { - val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp) - val clockPadding by animateDpAsState( - if (isWidgetsScrollZero) 64.dp else 0.dp - ) - var size by remember { mutableStateOf(IntSize.Zero) } + val widthPx = width.toPixels() - val clockHeight by remember { - derivedStateOf { - with(density) { size.height.toDp() } - (64.dp - clockPadding) - } - } - WidgetColumn( + CompositionLocalProvider( + LocalOverScrollConfiguration provides null + ) { + + Row( modifier = Modifier - .fillMaxSize() - .onSizeChanged { - size = it + .requiredWidth(width * 2) + .fillMaxHeight() + .swipeable( + swipeableState, + orientation = Orientation.Horizontal, + anchors = mapOf( + -widthPx / 2f to Page.Search, + widthPx / 2f to Page.Widgets, + ), + thresholds = { _, _ -> + FractionalThreshold(0.5f) + }, + enabled = !isWidgetEditMode + ) + .offset { + IntOffset(swipeableState.offset.value.roundToInt(), 0) + } + ) { + + + val editModePadding by animateDpAsState(if (isWidgetEditMode) 56.dp else 0.dp) + + val clockPadding by animateDpAsState( + if (isWidgetsScrollZero) 64.dp else 0.dp + ) + + val clockHeight by remember { + derivedStateOf { + height - (64.dp - clockPadding) } - .verticalScroll(widgetsScrollState) - .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) - .padding(top = editModePadding), - clockHeight = { clockHeight }, - clockBottomPadding = { clockPadding }, - editMode = isWidgetEditMode, - onEditModeChange = { - viewModel.setWidgetEditMode(it) } - ) + + WidgetColumn( + modifier = Modifier + .requiredWidth(width) + .fillMaxHeight() + .verticalScroll(widgetsScrollState) + .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) + .padding(top = editModePadding), + clockHeight = { clockHeight }, + clockBottomPadding = { clockPadding }, + editMode = isWidgetEditMode, + onEditModeChange = { + viewModel.setWidgetEditMode(it) + } + ) + + + val websearches by searchVM.websearchResults.observeAsState(emptyList()) + val webSearchPadding by animateDpAsState( + if (websearches.isEmpty()) 0.dp else 48.dp + ) + SearchColumn( + modifier = Modifier + .requiredWidth(width) + .fillMaxHeight() + .verticalScroll(searchScrollState, reverseScrolling = true) + .imePadding() + .padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 64.dp) + .padding(bottom = webSearchPadding), + reverse = true, + ) + } } } AnimatedVisibility(visible = isWidgetEditMode, @@ -215,7 +253,7 @@ fun PagerScaffold( val searchBarLevel by remember { derivedStateOf { when { - pagerState.isScrollInProgress -> SearchBarLevel.Raised + swipeableState.direction != 0f -> SearchBarLevel.Raised !isSearchOpen && isWidgetsScrollZero -> SearchBarLevel.Resting isSearchOpen && searchScrollState.value == 0 -> SearchBarLevel.Active else -> SearchBarLevel.Raised @@ -242,5 +280,9 @@ fun PagerScaffold( reverse = true ) } +} +private enum class Page { + Widgets, + Search } \ No newline at end of file diff --git a/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index ba583df3..c55719e1 100644 --- a/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -216,7 +216,6 @@ fun PullDownScaffold( val height by remember { derivedStateOf { maxHeight } } - Log.d("MM20", "PullDownScaffold recompose, $maxHeight") CompositionLocalProvider( LocalOverScrollConfiguration provides null ) {