System bar scrim improvements

pull/3823/head
Alex Baker 3 months ago
parent 655cdc1a9d
commit 5a1560e513

@ -123,10 +123,11 @@ class MainActivity : AppCompatActivity() {
lightScrim = Color.TRANSPARENT, lightScrim = Color.TRANSPARENT,
darkScrim = Color.TRANSPARENT darkScrim = Color.TRANSPARENT
), ),
navigationBarStyle = SystemBarStyle.auto( navigationBarStyle = if (theme.themeBase.isDarkTheme(this)) {
lightScrim = Color.TRANSPARENT, SystemBarStyle.dark(Color.TRANSPARENT)
darkScrim = Color.TRANSPARENT } else {
) SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)
}
) )
setContent { setContent {

@ -22,7 +22,6 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.LAYOUT_DIRECTION_LTR import android.view.ViewGroup.LAYOUT_DIRECTION_LTR
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@ -338,7 +337,9 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
right = endInset, right = endInset,
) )
binding.bottomAppBar.updatePadding(bottom = bottomInset) binding.bottomAppBar.updatePadding(bottom = bottomInset)
(binding.fab.layoutParams as MarginLayoutParams).bottomMargin = bottomInset / 2 val scrimLayoutParams = binding.systemBarScrim.layoutParams
scrimLayoutParams.height = bottomInset
binding.systemBarScrim.layoutParams = scrimLayoutParams
} }
@OptIn(ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
@ -365,6 +366,14 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
listViewModel.setFilter(filter) listViewModel.setFilter(filter)
(recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false (recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.layoutManager = LinearLayoutManager(context)
val baseFooterHeight = resources.getDimensionPixelSize(R.dimen.task_list_footer_height)
val additionalFabSpace = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
56f,
resources.displayMetrics
).toInt()
recyclerView.updatePadding(bottom = baseFooterHeight + additionalFabSpace)
lifecycleScope.launch { lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
listViewModel.updateBannerState() listViewModel.updateBannerState()
@ -394,6 +403,11 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
binding.bottomAppBar.performShow() binding.bottomAppBar.performShow()
} }
} }
val typedValue = TypedValue()
requireContext().theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true)
val scrimColor = typedValue.data
binding.systemBarScrim.setBackgroundColor((scrimColor and 0x00FFFFFF) or 0xCC000000.toInt()) // 80% opacity
with (binding.fab) { with (binding.fab) {
backgroundTintList = ColorStateList.valueOf(themeColor.primaryColor) backgroundTintList = ColorStateList.valueOf(themeColor.primaryColor)
imageTintList = ColorStateList.valueOf(themeColor.colorOnPrimary) imageTintList = ColorStateList.valueOf(themeColor.colorOnPrimary)

@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.layout.windowInsetsTopHeight
import androidx.compose.material3.DrawerState import androidx.compose.material3.DrawerState
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@ -121,173 +123,194 @@ fun HomeScreen(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
TaskListDrawer( Box(modifier = Modifier.fillMaxSize()) {
arrangement = if (state.menuQuery.isBlank()) Arrangement.Top else Arrangement.Bottom, TaskListDrawer(
filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems, arrangement = if (state.menuQuery.isBlank()) Arrangement.Top else Arrangement.Bottom,
onClick = { filters = if (state.menuQuery.isNotEmpty()) state.searchItems else state.drawerItems,
when (it) { onClick = {
is DrawerItem.Filter -> { when (it) {
viewModel.setFilter(it.filter) is DrawerItem.Filter -> {
scope.launch { viewModel.setFilter(it.filter)
drawerState.close() scope.launch {
keyboard?.hide() drawerState.close()
keyboard?.hide()
}
} }
}
is DrawerItem.Header -> { is DrawerItem.Header -> {
viewModel.toggleCollapsed(it.header) viewModel.toggleCollapsed(it.header)
}
} }
} },
}, onAddClick = {
onAddClick = { scope.launch {
scope.launch { drawerState.close()
drawerState.close() when (it.header.addIntentRc) {
when (it.header.addIntentRc) { FilterProvider.REQUEST_NEW_FILTER ->
FilterProvider.REQUEST_NEW_FILTER -> showNewFilterDialog()
showNewFilterDialog()
REQUEST_NEW_PLACE -> REQUEST_NEW_PLACE ->
newList.launch(Intent(context, LocationPickerActivity::class.java)) newList.launch(Intent(context, LocationPickerActivity::class.java))
REQUEST_NEW_TAGS -> REQUEST_NEW_TAGS ->
newList.launch(Intent(context, TagSettingsActivity::class.java)) newList.launch(Intent(context, TagSettingsActivity::class.java))
REQUEST_NEW_LIST -> REQUEST_NEW_LIST ->
when (it.header.subheaderType) { when (it.header.subheaderType) {
NavigationDrawerSubheader.SubheaderType.CALDAV, NavigationDrawerSubheader.SubheaderType.CALDAV,
NavigationDrawerSubheader.SubheaderType.TASKS -> NavigationDrawerSubheader.SubheaderType.TASKS ->
viewModel viewModel
.getAccount(it.header.id.toLong()) .getAccount(it.header.id.toLong())
?.let { ?.let {
newList.launch( newList.launch(
Intent(context, it.listSettingsClass()) Intent(context, it.listSettingsClass())
.putExtra(EXTRA_CALDAV_ACCOUNT, it) .putExtra(EXTRA_CALDAV_ACCOUNT, it)
) )
} }
else -> {} else -> {}
} }
else -> Timber.e("Unhandled request code: $it") else -> Timber.e("Unhandled request code: $it")
}
} }
} },
}, onErrorClick = {
onErrorClick = { context.startActivity(Intent(context, MainPreferences::class.java))
context.startActivity(Intent(context, MainPreferences::class.java)) },
}, searchBar = {
searchBar = { MenuSearchBar(
MenuSearchBar( begForMoney = state.begForMoney,
begForMoney = state.begForMoney, onDrawerAction = {
onDrawerAction = { scope.launch {
scope.launch { drawerState.close()
drawerState.close() when (it) {
when (it) { DrawerAction.PURCHASE ->
DrawerAction.PURCHASE -> if (TasksApplication.IS_GENERIC)
if (TasksApplication.IS_GENERIC) context.openUri(R.string.url_donate)
context.openUri(R.string.url_donate) else
else context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity( context.startActivity(
Intent( Intent(
context, context,
PurchaseActivity::class.java HelpAndFeedback::class.java
) )
) )
}
DrawerAction.HELP_AND_FEEDBACK ->
context.startActivity(
Intent(
context,
HelpAndFeedback::class.java
)
)
} }
} },
}, query = state.menuQuery,
query = state.menuQuery, onQueryChange = { viewModel.queryMenu(it) },
onQueryChange = { viewModel.queryMenu(it) }, )
) },
}, )
)
SystemBarScrim(
modifier = Modifier
.windowInsetsTopHeight(WindowInsets.systemBars)
.align(Alignment.TopCenter)
)
SystemBarScrim(
modifier = Modifier
.windowInsetsBottomHeight(WindowInsets.systemBars)
.align(Alignment.BottomCenter),
)
}
} }
} }
) { ) {
val scope = rememberCoroutineScope() Box(modifier = Modifier.fillMaxSize()) {
ListDetailPaneScaffold( val scope = rememberCoroutineScope()
directive = navigator.scaffoldDirective, ListDetailPaneScaffold(
value = navigator.scaffoldValue, directive = navigator.scaffoldDirective,
listPane = { value = navigator.scaffoldValue,
key (state.filter) { listPane = {
val fragment = remember { mutableStateOf<TaskListFragment?>(null) } key (state.filter) {
val keyboardOpen = rememberImeState() val fragment = remember { mutableStateOf<TaskListFragment?>(null) }
AndroidFragment<TaskListFragment>( val keyboardOpen = rememberImeState()
fragmentState = rememberFragmentState(), AndroidFragment<TaskListFragment>(
arguments = remember(state.filter) { fragmentState = rememberFragmentState(),
Bundle() arguments = remember(state.filter) {
.apply { putParcelable(EXTRA_FILTER, state.filter) } Bundle()
}, .apply { putParcelable(EXTRA_FILTER, state.filter) }
modifier = Modifier },
.fillMaxSize() modifier = Modifier
.imePadding(), .fillMaxSize()
) { tlf -> .imePadding(),
fragment.value = tlf ) { tlf ->
tlf.applyInsets(windowInsets.value) fragment.value = tlf
tlf.setNavigationClickListener { tlf.applyInsets(windowInsets.value)
scope.launch { drawerState.open() } tlf.setNavigationClickListener {
scope.launch { drawerState.open() }
}
}
LaunchedEffect(fragment, windowInsets, keyboardOpen.value) {
fragment.value?.applyInsets(
if (keyboardOpen.value) {
PaddingValues(
top = windowInsets.value.calculateTopPadding(),
)
} else {
windowInsets.value
}
)
} }
} }
LaunchedEffect(fragment, windowInsets, keyboardOpen.value) { },
fragment.value?.applyInsets( detailPane = {
if (keyboardOpen.value) { val direction = LocalLayoutDirection.current
PaddingValues( Box(
top = windowInsets.value.calculateTopPadding(), modifier = Modifier
.fillMaxSize()
.padding(
top = windowInsets.value.calculateTopPadding(),
start = windowInsets.value.calculateStartPadding(direction),
end = windowInsets.value.calculateEndPadding(direction),
bottom = if (rememberImeState().value)
WindowInsets.ime.asPaddingValues().calculateBottomPadding()
else
windowInsets.value.calculateBottomPadding()
),
contentAlignment = Alignment.Center,
) {
if (state.task == null) {
if (isListVisible && isDetailVisible) {
Icon(
painter = painterResource(org.tasks.kmp.R.drawable.ic_launcher_no_shadow_foreground),
contentDescription = null,
modifier = Modifier.size(192.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
} else {
key(state.task) {
AndroidFragment<TaskEditFragment>(
fragmentState = rememberFragmentState(),
arguments = remember(state.task) {
Bundle()
.apply { putParcelable(EXTRA_TASK, state.task) }
},
modifier = Modifier.fillMaxSize(),
) )
} else {
windowInsets.value
} }
)
}
}
},
detailPane = {
val direction = LocalLayoutDirection.current
Box(
modifier = Modifier
.fillMaxSize()
.padding(
top = windowInsets.value.calculateTopPadding(),
start = windowInsets.value.calculateStartPadding(direction),
end = windowInsets.value.calculateEndPadding(direction),
bottom = if (rememberImeState().value)
WindowInsets.ime.asPaddingValues().calculateBottomPadding()
else
windowInsets.value.calculateBottomPadding()
),
contentAlignment = Alignment.Center,
) {
if (state.task == null) {
if (isListVisible && isDetailVisible) {
Icon(
painter = painterResource(org.tasks.kmp.R.drawable.ic_launcher_no_shadow_foreground),
contentDescription = null,
modifier = Modifier.size(192.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
} else {
key(state.task) {
AndroidFragment<TaskEditFragment>(
fragmentState = rememberFragmentState(),
arguments = remember(state.task) {
Bundle()
.apply { putParcelable(EXTRA_TASK, state.task) }
},
modifier = Modifier.fillMaxSize(),
)
} }
} }
} },
}, )
)
SystemBarScrim(
modifier = Modifier
.windowInsetsTopHeight(WindowInsets.systemBars)
.align(Alignment.TopCenter),
)
}
} }
} }
} }

@ -0,0 +1,22 @@
package org.tasks.compose.home
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@Composable
fun SystemBarScrim(
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colorScheme.background,
) {
Box(
modifier = Modifier
.fillMaxWidth()
.background(color.copy(alpha = 0.8f))
.then(modifier)
)
}

@ -68,6 +68,13 @@
</LinearLayout> </LinearLayout>
<View
android:id="@+id/systemBarScrim"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
android:visibility="visible" />
<com.google.android.material.bottomappbar.BottomAppBar <com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomAppBar" android:id="@+id/bottomAppBar"
style="@style/Widget.Material3.BottomAppBar" style="@style/Widget.Material3.BottomAppBar"

@ -108,7 +108,7 @@ fun TaskListDrawer(
placeable.place(0, 0) placeable.place(0, 0)
} }
}, },
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.background,
scrollBehavior = bottomAppBarScrollBehavior scrollBehavior = bottomAppBarScrollBehavior
) { ) {
searchBar() searchBar()

Loading…
Cancel
Save