mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 23:36:47 +00:00
Make settings layout span under system bars
This commit is contained in:
@@ -2,6 +2,7 @@ buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.4.32'
|
||||
compose_version = '1.0.0-beta05'
|
||||
accompanist_version = '0.9.1'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -290,7 +291,9 @@ dependencies {
|
||||
implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
|
||||
implementation "androidx.compose.compiler:compiler:$compose_version"
|
||||
implementation "androidx.navigation:navigation-compose:1.0.0-alpha10"
|
||||
implementation "com.google.accompanist:accompanist-glide:0.7.1"
|
||||
implementation "com.google.accompanist:accompanist-glide:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-insets:$accompanist_version"
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,21 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.core.view.WindowCompat
|
||||
import app.lawnchair.ui.theme.LawnchairTheme
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
|
||||
class PreferenceActivity : ComponentActivity() {
|
||||
@ExperimentalAnimationApi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
setContent {
|
||||
LawnchairTheme {
|
||||
Preferences(window = this.window)
|
||||
ProvideWindowInsets {
|
||||
Preferences()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,24 +17,19 @@
|
||||
package app.lawnchair.ui.preferences
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Window
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import app.lawnchair.ui.preferences.about.About
|
||||
import app.lawnchair.ui.preferences.about.aboutGraph
|
||||
import app.lawnchair.ui.preferences.components.PreferenceCategoryList
|
||||
import app.lawnchair.ui.preferences.components.SystemUi
|
||||
@@ -122,20 +117,15 @@ val LocalPreferenceInteractor = compositionLocalOf<PreferenceInteractor> {
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel>(), window: Window) {
|
||||
fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel>()) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
SystemUi(window = window)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
) {
|
||||
SystemUi()
|
||||
Surface(color = MaterialTheme.colors.background) {
|
||||
CompositionLocalProvider(
|
||||
LocalNavController provides navController,
|
||||
LocalPreferenceInteractor provides interactor,
|
||||
) {
|
||||
TopBar()
|
||||
NavHost(navController = navController, startDestination = "preferences") {
|
||||
composable(route = Routes.PREFERENCES) {
|
||||
pageMeta.provide(Meta(title = stringResource(id = R.string.settings)))
|
||||
@@ -148,6 +138,7 @@ fun Preferences(interactor: PreferenceInteractor = viewModel<PreferenceViewModel
|
||||
folderGraph(route = Routes.FOLDERS)
|
||||
aboutGraph(route = Routes.ABOUT)
|
||||
}
|
||||
TopBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -36,8 +34,9 @@ import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import app.lawnchair.ui.preferences.components.PreferenceGroup
|
||||
import app.lawnchair.ui.preferences.components.ClickListenerPreference
|
||||
import app.lawnchair.ui.preferences.components.PreferenceGroup
|
||||
import app.lawnchair.ui.preferences.components.PreferenceLayout
|
||||
import app.lawnchair.ui.preferences.preferenceGraph
|
||||
import app.lawnchair.util.Meta
|
||||
import app.lawnchair.util.pageMeta
|
||||
@@ -143,11 +142,8 @@ fun About() {
|
||||
val context = LocalContext.current
|
||||
|
||||
pageMeta.provide(Meta(title = stringResource(id = R.string.about_label)))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally
|
||||
PreferenceLayout(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.requiredHeight(24.dp))
|
||||
Image(
|
||||
@@ -239,6 +235,5 @@ fun About() {
|
||||
}
|
||||
})
|
||||
}
|
||||
Spacer(modifier = Modifier.requiredHeight(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,9 @@
|
||||
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.compose.navigate
|
||||
@@ -32,9 +29,7 @@ fun PreferenceCategoryList(navController: NavController) {
|
||||
val context = LocalContext.current
|
||||
val categories = remember { getPreferenceCategories(context) }
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxHeight()
|
||||
) {
|
||||
PreferenceLayoutLazyColumn {
|
||||
items(categories) { item ->
|
||||
PreferenceCategoryListItem(
|
||||
label = item.label,
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.lawnchair.util.Meta
|
||||
import app.lawnchair.util.pageMeta
|
||||
import com.google.accompanist.insets.LocalWindowInsets
|
||||
import com.google.accompanist.insets.toPaddingValues
|
||||
|
||||
@Composable
|
||||
fun PreferenceLayout(content: @Composable () -> Unit) {
|
||||
fun PreferenceLayout(
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
ProvideTopBarFloatingState(scrollState.value > 0)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 16.dp)
|
||||
.verticalScroll(scrollState)
|
||||
.padding(preferenceLayoutPadding()),
|
||||
verticalArrangement = verticalArrangement,
|
||||
horizontalAlignment = horizontalAlignment
|
||||
) {
|
||||
content()
|
||||
}
|
||||
@@ -23,8 +41,25 @@ fun PreferenceLayout(content: @Composable () -> Unit) {
|
||||
|
||||
@Composable
|
||||
fun PreferenceLayoutLazyColumn(modifier: Modifier = Modifier, content: LazyListScope.() -> Unit) {
|
||||
LazyColumn(modifier = modifier.fillMaxHeight()) {
|
||||
val scrollState = rememberLazyListState()
|
||||
ProvideTopBarFloatingState(scrollState.firstVisibleItemIndex > 0 || scrollState.firstVisibleItemScrollOffset > 0)
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxHeight(),
|
||||
contentPadding = preferenceLayoutPadding(),
|
||||
state = scrollState
|
||||
) {
|
||||
content()
|
||||
item { Spacer(modifier = Modifier.height(16.dp)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun preferenceLayoutPadding() = LocalWindowInsets.current.systemBars.toPaddingValues(
|
||||
additionalTop = topBarSize,
|
||||
additionalBottom = 16.dp
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun ProvideTopBarFloatingState(scrolled: Boolean) {
|
||||
val meta = remember(scrolled) { Meta(topBarFloating = scrolled) }
|
||||
pageMeta.provide(meta)
|
||||
}
|
||||
|
||||
@@ -16,30 +16,41 @@
|
||||
|
||||
package app.lawnchair.ui.preferences.components
|
||||
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import app.lawnchair.ui.theme.LawnchairTheme
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
|
||||
@Composable
|
||||
fun SystemUi(window: Window) =
|
||||
LawnchairTheme {
|
||||
window.statusBarColor = MaterialTheme.colors.background.toArgb()
|
||||
window.navigationBarColor = MaterialTheme.colors.background.toArgb()
|
||||
fun SystemUi() {
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val useDarkIcons = MaterialTheme.colors.isLight
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
if (MaterialTheme.colors.background.luminance() > 0.5f) {
|
||||
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or
|
||||
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
}
|
||||
SideEffect {
|
||||
// Update all of the system bar colors to be transparent, and use
|
||||
// dark icons if we're in light theme
|
||||
systemUiController.setSystemBarsColor(
|
||||
color = Color.Transparent,
|
||||
darkIcons = useDarkIcons
|
||||
)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
if (MaterialTheme.colors.background.luminance() > 0.5f && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or
|
||||
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
}
|
||||
}
|
||||
// setStatusBarsColor() and setNavigationBarsColor() also exist
|
||||
}
|
||||
}
|
||||
// LawnchairTheme {
|
||||
// window.statusBarColor = MaterialTheme.colors.background.toArgb()
|
||||
// window.navigationBarColor = MaterialTheme.colors.background.toArgb()
|
||||
//
|
||||
// @Suppress("DEPRECATION")
|
||||
// if (MaterialTheme.colors.background.luminance() > 0.5f) {
|
||||
// window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or
|
||||
// View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
|
||||
// }
|
||||
//
|
||||
// @Suppress("DEPRECATION")
|
||||
// if (MaterialTheme.colors.background.luminance() > 0.5f && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or
|
||||
// View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -18,8 +18,11 @@ package app.lawnchair.ui.preferences.components
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.Icon
|
||||
@@ -30,10 +33,15 @@ import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.ArrowForward
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -42,21 +50,21 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import app.lawnchair.ui.preferences.LocalNavController
|
||||
import app.lawnchair.ui.preferences.Routes
|
||||
import app.lawnchair.util.pageMeta
|
||||
import com.google.accompanist.insets.statusBarsPadding
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun TopBar() {
|
||||
fun TopBar() = pageMeta.consume { state ->
|
||||
val navController = LocalNavController.current
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE)
|
||||
|
||||
pageMeta.consume { state ->
|
||||
TopBarSurface(floating = state.topBarFloating) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.background(MaterialTheme.colors.background)
|
||||
.height(topBarSize)
|
||||
) {
|
||||
AnimatedVisibility(visible = currentRoute != Routes.PREFERENCES && currentRoute != null) {
|
||||
Box(
|
||||
@@ -85,6 +93,35 @@ fun TopBar() {
|
||||
}
|
||||
}
|
||||
|
||||
val shadowColors = listOf(Color(0, 0, 0, 31), Color.Transparent)
|
||||
|
||||
@Composable
|
||||
fun TopBarSurface(floating: Boolean, content: @Composable () -> Unit) {
|
||||
val normalColor = MaterialTheme.colors.background.copy(alpha = 0.9f)
|
||||
val floatingColor = MaterialTheme.colors.surface.copy(alpha = 0.9f)
|
||||
val color by animateColorAsState(if (floating) floatingColor else normalColor)
|
||||
val shadowAlpha by animateFloatAsState(if (floating) 1f else 0f)
|
||||
Column {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(color)
|
||||
.statusBarsPadding()
|
||||
.pointerInput(remember { MutableInteractionSource() }) {
|
||||
// consume touch
|
||||
}
|
||||
) {
|
||||
content()
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(3.dp)
|
||||
.alpha(shadowAlpha)
|
||||
.background(Brush.verticalGradient(shadowColors))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun backIcon(): ImageVector {
|
||||
return if (LocalLayoutDirection.current == LayoutDirection.Ltr) {
|
||||
@@ -93,3 +130,5 @@ fun backIcon(): ImageVector {
|
||||
Icons.Rounded.ArrowForward
|
||||
}
|
||||
}
|
||||
|
||||
val topBarSize = 56.dp
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package app.lawnchair.util
|
||||
|
||||
class MetaState(val title: String)
|
||||
class Meta(val title: String?)
|
||||
class MetaState(val title: String, val topBarFloating: Boolean)
|
||||
class Meta(val title: String? = null, val topBarFloating: Boolean? = null)
|
||||
|
||||
private fun getTitleFromPropsList(propsList: List<Meta>): String {
|
||||
return propsList.lastOrNull()?.title ?: ""
|
||||
private fun <T> getFromPropsList(propsList: List<Meta>, defaultValue: T, extractor: (meta: Meta) -> T?): T {
|
||||
return propsList
|
||||
.asSequence()
|
||||
.map(extractor)
|
||||
.lastOrNull { it != null } ?: defaultValue
|
||||
}
|
||||
|
||||
val pageMeta = createSideEffect<MetaState, Meta> { propsList ->
|
||||
MetaState(title = getTitleFromPropsList(propsList))
|
||||
MetaState(
|
||||
title = getFromPropsList(propsList, "", Meta::title),
|
||||
topBarFloating = getFromPropsList(propsList, false, Meta::topBarFloating),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user