Menu search bar on top for Android 10 and below

pull/2977/head
Alex Baker 4 months ago
parent d3421bd0c3
commit 8f4514fc78

@ -26,6 +26,7 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.AndroidUtilities.atLeastR
import com.todoroo.astrid.activity.TaskEditFragment.Companion.newTaskEditFragment
import com.todoroo.astrid.adapter.SubheaderClickHandler
import com.todoroo.astrid.dao.TaskDao
@ -51,6 +52,7 @@ import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.drawer.DrawerAction
import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao
@ -145,7 +147,7 @@ class MainActivity : AppCompatActivity() {
}
val scope = rememberCoroutineScope()
TaskListDrawer(
begForMoney = state.begForMoney,
bottomSearchBar = atLeastR(),
filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems,
onClick = {
when (it) {
@ -230,6 +232,12 @@ class MainActivity : AppCompatActivity() {
}
}
},
onErrorClick = {
context.startActivity(Intent(context, MainPreferences::class.java))
},
searchBar = {
MenuSearchBar(
begForMoney = state.begForMoney,
onDrawerAction = {
viewModel.closeDrawer()
when (it) {
@ -261,12 +269,11 @@ class MainActivity : AppCompatActivity() {
)
}
},
onErrorClick = {
context.startActivity(Intent(context, MainPreferences::class.java))
},
query = state.menuQuery,
onQueryChange = { viewModel.queryMenu(it) },
)
},
)
}
}
}

@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.collections.immutable.persistentListOf
import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.filters.FilterImpl
import org.tasks.filters.NavigationDrawerSubheader
@ -17,7 +18,15 @@ import org.tasks.themes.TasksTheme
fun MenuPreview() {
TasksTheme {
TaskListDrawer(
searchBar = {
MenuSearchBar(
begForMoney = true,
onDrawerAction = {},
query = "",
onQueryChange = {}
)
},
bottomSearchBar = true,
filters = persistentListOf(
DrawerItem.Filter(
title = "My Tasks",
@ -39,11 +48,8 @@ fun MenuPreview() {
)
),
onClick = {},
onDrawerAction = {},
onAddClick = {},
onErrorClick = {},
query = "",
onQueryChange = {},
)
}
}

@ -39,6 +39,8 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -67,25 +69,32 @@ import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TaskListDrawer(
begForMoney: Boolean,
bottomSearchBar: Boolean,
filters: ImmutableList<DrawerItem>,
onClick: (DrawerItem) -> Unit,
onDrawerAction: (DrawerAction) -> Unit,
onAddClick: (DrawerItem.Header) -> Unit,
onErrorClick: () -> Unit,
query: String,
onQueryChange: (String) -> Unit,
searchBar: @Composable RowScope.() -> Unit,
) {
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
modifier = Modifier
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection),
.nestedScroll(
if (bottomSearchBar)
bottomAppBarScrollBehavior.nestedScrollConnection
else
topAppBarScrollBehavior.nestedScrollConnection
),
bottomBar = {
if (bottomSearchBar) {
BottomAppBar(
modifier = Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
bottomAppBarScrollBehavior.state.heightOffsetLimit = -placeable.height.toFloat()
val height = placeable.height + bottomAppBarScrollBehavior.state.heightOffset
bottomAppBarScrollBehavior.state.heightOffsetLimit =
-placeable.height.toFloat()
val height =
placeable.height + bottomAppBarScrollBehavior.state.heightOffset
layout(placeable.width, height.roundToInt().coerceAtLeast(0)) {
placeable.place(0, 0)
}
@ -93,51 +102,23 @@ fun TaskListDrawer(
containerColor = MaterialTheme.colorScheme.surface,
scrollBehavior = bottomAppBarScrollBehavior
) {
var hasFocus by remember { mutableStateOf(false) }
SearchBar(
modifier = Modifier
.onFocusChanged { hasFocus = it.hasFocus }
.padding(start = 8.dp, end = if (hasFocus) 8.dp else 0.dp, bottom = 4.dp)
.weight(1f)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
),
text = query,
onTextChange = { onQueryChange(it) },
placeHolder = stringResource(Res.string.search),
onCloseClicked = { onQueryChange("") },
onSearchClicked = {
// TODO: close keyboard
},
)
if (!hasFocus) {
if (begForMoney) {
IconButton(onClick = { onDrawerAction(DrawerAction.PURCHASE) }) {
Icon(
imageVector = Icons.Outlined.AttachMoney,
contentDescription = stringResource(Res.string.subscribe),
tint = MaterialTheme.colorScheme.onSurface,
)
searchBar()
}
}
IconButton(onClick = { onDrawerAction(DrawerAction.HELP_AND_FEEDBACK) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
contentDescription = stringResource(Res.string.help_and_feedback),
tint = MaterialTheme.colorScheme.onSurface,
)
}
IconButton(onClick = { onDrawerAction(DrawerAction.SETTINGS) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(Res.string.settings),
tint = MaterialTheme.colorScheme.onSurface,
)
},
topBar = {
if (!bottomSearchBar) {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
scrollBehavior = topAppBarScrollBehavior,
title = {
Row {
searchBar()
}
}
)
}
}
) { contentPadding ->
@ -145,14 +126,17 @@ fun TaskListDrawer(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
bottom = maxOf(
top = if (bottomSearchBar) 0.dp else contentPadding.calculateTopPadding(),
bottom = if (bottomSearchBar)
maxOf(
WindowInsets.mandatorySystemGestures
.asPaddingValues()
.calculateBottomPadding(),
contentPadding.calculateBottomPadding()
)
) else
48.dp
),
verticalArrangement = Arrangement.Bottom,
verticalArrangement = if (bottomSearchBar) Arrangement.Bottom else Arrangement.Top,
) {
items(items = filters, key = { it.key() }) {
when (it) {
@ -294,3 +278,61 @@ private fun MenuRow(
content = content
)
}
@Composable
fun RowScope.MenuSearchBar(
begForMoney: Boolean,
onDrawerAction: (DrawerAction) -> Unit,
query: String,
onQueryChange: (String) -> Unit,
) {
var hasFocus by remember { mutableStateOf(false) }
SearchBar(
modifier = Modifier
.onFocusChanged { hasFocus = it.hasFocus }
.padding(
start = 8.dp,
end = if (hasFocus) 8.dp else 0.dp,
bottom = 4.dp
)
.weight(1f)
.animateContentSize(
animationSpec = spring(
dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessMedium
)
),
text = query,
onTextChange = { onQueryChange(it) },
placeHolder = stringResource(Res.string.search),
onCloseClicked = { onQueryChange("") },
onSearchClicked = {
// TODO: close keyboard
},
)
if (!hasFocus) {
if (begForMoney) {
IconButton(onClick = { onDrawerAction(DrawerAction.PURCHASE) }) {
Icon(
imageVector = Icons.Outlined.AttachMoney,
contentDescription = stringResource(Res.string.subscribe),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
IconButton(onClick = { onDrawerAction(DrawerAction.HELP_AND_FEEDBACK) }) {
Icon(
imageVector = Icons.AutoMirrored.Outlined.HelpOutline,
contentDescription = stringResource(Res.string.help_and_feedback),
tint = MaterialTheme.colorScheme.onSurface,
)
}
IconButton(onClick = { onDrawerAction(DrawerAction.SETTINGS) }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(Res.string.settings),
tint = MaterialTheme.colorScheme.onSurface,
)
}
}
}

Loading…
Cancel
Save