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.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.todoroo.andlib.utility.AndroidUtilities 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.activity.TaskEditFragment.Companion.newTaskEditFragment
import com.todoroo.astrid.adapter.SubheaderClickHandler import com.todoroo.astrid.adapter.SubheaderClickHandler
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
@ -51,6 +52,7 @@ import org.tasks.billing.PurchaseActivity
import org.tasks.caldav.BaseCaldavCalendarSettingsActivity import org.tasks.caldav.BaseCaldavCalendarSettingsActivity
import org.tasks.compose.drawer.DrawerAction import org.tasks.compose.drawer.DrawerAction
import org.tasks.compose.drawer.DrawerItem import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.data.dao.AlarmDao import org.tasks.data.dao.AlarmDao
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
@ -145,7 +147,7 @@ class MainActivity : AppCompatActivity() {
} }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
TaskListDrawer( TaskListDrawer(
begForMoney = state.begForMoney, bottomSearchBar = atLeastR(),
filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems, filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems,
onClick = { onClick = {
when (it) { when (it) {
@ -230,42 +232,47 @@ class MainActivity : AppCompatActivity() {
} }
} }
}, },
onDrawerAction = {
viewModel.closeDrawer()
when (it) {
DrawerAction.PURCHASE ->
if (Tasks.IS_GENERIC)
context.openUri(R.string.url_donate)
else
context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
DrawerAction.SETTINGS ->
settingsRequest.launch(
Intent(
context,
MainPreferences::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity(
Intent(
context,
HelpAndFeedback::class.java
)
)
}
},
onErrorClick = { onErrorClick = {
context.startActivity(Intent(context, MainPreferences::class.java)) context.startActivity(Intent(context, MainPreferences::class.java))
}, },
query = state.menuQuery, searchBar = {
onQueryChange = { viewModel.queryMenu(it) }, MenuSearchBar(
begForMoney = state.begForMoney,
onDrawerAction = {
viewModel.closeDrawer()
when (it) {
DrawerAction.PURCHASE ->
if (Tasks.IS_GENERIC)
context.openUri(R.string.url_donate)
else
context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
DrawerAction.SETTINGS ->
settingsRequest.launch(
Intent(
context,
MainPreferences::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity(
Intent(
context,
HelpAndFeedback::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 androidx.compose.ui.tooling.preview.Preview
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import org.tasks.compose.drawer.DrawerItem import org.tasks.compose.drawer.DrawerItem
import org.tasks.compose.drawer.MenuSearchBar
import org.tasks.compose.drawer.TaskListDrawer import org.tasks.compose.drawer.TaskListDrawer
import org.tasks.filters.FilterImpl import org.tasks.filters.FilterImpl
import org.tasks.filters.NavigationDrawerSubheader import org.tasks.filters.NavigationDrawerSubheader
@ -17,7 +18,15 @@ import org.tasks.themes.TasksTheme
fun MenuPreview() { fun MenuPreview() {
TasksTheme { TasksTheme {
TaskListDrawer( TaskListDrawer(
begForMoney = true, searchBar = {
MenuSearchBar(
begForMoney = true,
onDrawerAction = {},
query = "",
onQueryChange = {}
)
},
bottomSearchBar = true,
filters = persistentListOf( filters = persistentListOf(
DrawerItem.Filter( DrawerItem.Filter(
title = "My Tasks", title = "My Tasks",
@ -39,11 +48,8 @@ fun MenuPreview() {
) )
), ),
onClick = {}, onClick = {},
onDrawerAction = {},
onAddClick = {}, onAddClick = {},
onErrorClick = {}, onErrorClick = {},
query = "",
onQueryChange = {},
) )
} }
} }

@ -39,6 +39,8 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -67,77 +69,56 @@ import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun TaskListDrawer( fun TaskListDrawer(
begForMoney: Boolean, bottomSearchBar: Boolean,
filters: ImmutableList<DrawerItem>, filters: ImmutableList<DrawerItem>,
onClick: (DrawerItem) -> Unit, onClick: (DrawerItem) -> Unit,
onDrawerAction: (DrawerAction) -> Unit,
onAddClick: (DrawerItem.Header) -> Unit, onAddClick: (DrawerItem.Header) -> Unit,
onErrorClick: () -> Unit, onErrorClick: () -> Unit,
query: String, searchBar: @Composable RowScope.() -> Unit,
onQueryChange: (String) -> Unit,
) { ) {
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior() val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection), .nestedScroll(
if (bottomSearchBar)
bottomAppBarScrollBehavior.nestedScrollConnection
else
topAppBarScrollBehavior.nestedScrollConnection
),
bottomBar = { bottomBar = {
BottomAppBar( if (bottomSearchBar) {
modifier = Modifier.layout { measurable, constraints -> BottomAppBar(
val placeable = measurable.measure(constraints) modifier = Modifier.layout { measurable, constraints ->
bottomAppBarScrollBehavior.state.heightOffsetLimit = -placeable.height.toFloat() val placeable = measurable.measure(constraints)
val height = placeable.height + bottomAppBarScrollBehavior.state.heightOffset bottomAppBarScrollBehavior.state.heightOffsetLimit =
layout(placeable.width, height.roundToInt().coerceAtLeast(0)) { -placeable.height.toFloat()
placeable.place(0, 0) val height =
} placeable.height + bottomAppBarScrollBehavior.state.heightOffset
}, layout(placeable.width, height.roundToInt().coerceAtLeast(0)) {
containerColor = MaterialTheme.colorScheme.surface, placeable.place(0, 0)
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
}, },
) containerColor = MaterialTheme.colorScheme.surface,
if (!hasFocus) { scrollBehavior = bottomAppBarScrollBehavior
if (begForMoney) { ) {
IconButton(onClick = { onDrawerAction(DrawerAction.PURCHASE) }) { searchBar()
Icon( }
imageVector = Icons.Outlined.AttachMoney, }
contentDescription = stringResource(Res.string.subscribe), },
tint = MaterialTheme.colorScheme.onSurface, topBar = {
) if (!bottomSearchBar) {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface,
),
scrollBehavior = topAppBarScrollBehavior,
title = {
Row {
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,
)
}
}
} }
} }
) { contentPadding -> ) { contentPadding ->
@ -145,14 +126,17 @@ fun TaskListDrawer(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
contentPadding = PaddingValues( contentPadding = PaddingValues(
bottom = maxOf( top = if (bottomSearchBar) 0.dp else contentPadding.calculateTopPadding(),
bottom = if (bottomSearchBar)
maxOf(
WindowInsets.mandatorySystemGestures WindowInsets.mandatorySystemGestures
.asPaddingValues() .asPaddingValues()
.calculateBottomPadding(), .calculateBottomPadding(),
contentPadding.calculateBottomPadding() contentPadding.calculateBottomPadding()
) ) else
48.dp
), ),
verticalArrangement = Arrangement.Bottom, verticalArrangement = if (bottomSearchBar) Arrangement.Bottom else Arrangement.Top,
) { ) {
items(items = filters, key = { it.key() }) { items(items = filters, key = { it.key() }) {
when (it) { when (it) {
@ -294,3 +278,61 @@ private fun MenuRow(
content = content 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