Skip to content

Commit

Permalink
Merge branch 'improve-expand-animation-for-filterscreen-droid-1212'
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Aug 7, 2024
2 parents 8c7a83a + 46added commit 95fec4f
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ private fun PreviewExpandedEnabledExpandableComposeCell() {
fun ExpandableComposeCell(
title: String,
isExpanded: Boolean,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
testTag: String = "",
onCellClicked: (Boolean) -> Unit = {},
Expand All @@ -55,7 +56,7 @@ fun ExpandableComposeCell(
val bodyViewModifier = Modifier

BaseCell(
modifier = Modifier.testTag(testTag).focusProperties { canFocus = false },
modifier = modifier.testTag(testTag).focusProperties { canFocus = false },
headlineContent = {
BaseCellTitle(
title = title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ private fun PreviewSelectableCell() {
fun SelectableCell(
title: String,
isSelected: Boolean,
modifier: Modifier = Modifier,
isEnabled: Boolean = true,
iconContentDescription: String? = null,
selectedIcon: @Composable RowScope.() -> Unit = {
Expand All @@ -60,6 +61,7 @@ fun SelectableCell(
testTag: String = ""
) {
BaseCell(
modifier = modifier,
onCellClicked = onCellClicked,
isRowEnabled = isEnabled,
headlineContent = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package net.mullvad.mullvadvpn.compose.screen

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
Expand Down Expand Up @@ -36,6 +36,7 @@ import net.mullvad.mullvadvpn.compose.button.ApplyButton
import net.mullvad.mullvadvpn.compose.cell.CheckboxCell
import net.mullvad.mullvadvpn.compose.cell.ExpandableComposeCell
import net.mullvad.mullvadvpn.compose.cell.SelectableCell
import net.mullvad.mullvadvpn.compose.constant.ContentType
import net.mullvad.mullvadvpn.compose.extensions.itemWithDivider
import net.mullvad.mullvadvpn.compose.extensions.itemsWithDivider
import net.mullvad.mullvadvpn.compose.state.RelayFilterState
Expand Down Expand Up @@ -102,61 +103,45 @@ fun FilterScreen(
var ownershipExpanded by rememberSaveable { mutableStateOf(false) }

val backgroundColor = MaterialTheme.colorScheme.background

Scaffold(
modifier = Modifier.background(backgroundColor).systemBarsPadding().fillMaxSize(),
topBar = {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onBackClick) {
Icon(
painter = painterResource(id = R.drawable.icon_back),
contentDescription = null,
tint = Color.Unspecified,
)
}
Text(
text = stringResource(R.string.filter),
modifier = Modifier.weight(1f).padding(end = Dimens.titleIconSize),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimary
)
}
},
topBar = { TopBar(onBackClick = onBackClick) },
bottomBar = {
Box(
modifier =
Modifier.fillMaxWidth()
.padding(top = Dimens.screenVerticalMargin)
.clickable(enabled = false, onClick = onApplyClick)
.background(color = backgroundColor),
contentAlignment = Alignment.BottomCenter
) {
ApplyButton(
onClick = onApplyClick,
isEnabled = state.isApplyButtonEnabled,
modifier =
Modifier.padding(
start = Dimens.sideMargin,
end = Dimens.sideMargin,
bottom = Dimens.screenVerticalMargin
),
)
}
BottomBar(
isApplyButtonEnabled = state.isApplyButtonEnabled,
backgroundColor = backgroundColor,
onApplyClick = onApplyClick
)
},
) { contentPadding ->
LazyColumn(modifier = Modifier.padding(contentPadding).fillMaxSize()) {
itemWithDivider { OwnershipHeader(ownershipExpanded) { ownershipExpanded = it } }
itemWithDivider(key = Keys.OWNERSHIP_TITLE, contentType = ContentType.HEADER) {
OwnershipHeader(ownershipExpanded) { ownershipExpanded = it }
}
if (ownershipExpanded) {
item { AnyOwnership(state, onSelectedOwnership) }
itemsWithDivider(state.filteredOwnershipByProviders) { ownership ->
item(key = Keys.OWNERSHIP_ALL, contentType = ContentType.ITEM) {
AnyOwnership(state, onSelectedOwnership)
}
itemsWithDivider(
key = { it.name },
contentType = { ContentType.ITEM },
items = state.filteredOwnershipByProviders
) { ownership ->
Ownership(ownership, state, onSelectedOwnership)
}
}
itemWithDivider { ProvidersHeader(providerExpanded) { providerExpanded = it } }
itemWithDivider(key = Keys.PROVIDERS_TITLE, contentType = ContentType.HEADER) {
ProvidersHeader(providerExpanded) { providerExpanded = it }
}
if (providerExpanded) {
itemWithDivider { AllProviders(state, onAllProviderCheckChange) }
itemsWithDivider(state.filteredProvidersByOwnership) { provider ->
itemWithDivider(key = Keys.PROVIDERS_ALL, contentType = ContentType.ITEM) {
AllProviders(state, onAllProviderCheckChange)
}
itemsWithDivider(
key = { it.providerId.value },
contentType = { ContentType.ITEM },
items = state.filteredProvidersByOwnership
) { provider ->
Provider(provider, state, onSelectedProvider)
}
}
Expand All @@ -165,79 +150,138 @@ fun FilterScreen(
}

@Composable
private fun OwnershipHeader(expanded: Boolean, onToggleExpanded: (Boolean) -> Unit) {
private fun LazyItemScope.OwnershipHeader(expanded: Boolean, onToggleExpanded: (Boolean) -> Unit) {
ExpandableComposeCell(
title = stringResource(R.string.ownership),
isExpanded = expanded,
isEnabled = true,
onInfoClicked = null,
onCellClicked = { onToggleExpanded(!expanded) }
onCellClicked = { onToggleExpanded(!expanded) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun AnyOwnership(
private fun LazyItemScope.AnyOwnership(
state: RelayFilterState,
onSelectedOwnership: (ownership: Ownership?) -> Unit
) {
SelectableCell(
title = stringResource(id = R.string.any),
isSelected = state.selectedOwnership == null,
onCellClicked = { onSelectedOwnership(null) }
onCellClicked = { onSelectedOwnership(null) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun Ownership(
private fun LazyItemScope.Ownership(
ownership: Ownership,
state: RelayFilterState,
onSelectedOwnership: (ownership: Ownership?) -> Unit
) {
SelectableCell(
title = stringResource(id = ownership.stringResource()),
isSelected = ownership == state.selectedOwnership,
onCellClicked = { onSelectedOwnership(ownership) }
onCellClicked = { onSelectedOwnership(ownership) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun ProvidersHeader(expanded: Boolean, onToggleExpanded: (Boolean) -> Unit) {
private fun LazyItemScope.ProvidersHeader(expanded: Boolean, onToggleExpanded: (Boolean) -> Unit) {
ExpandableComposeCell(
title = stringResource(R.string.providers),
isExpanded = expanded,
isEnabled = true,
onInfoClicked = null,
onCellClicked = { onToggleExpanded(!expanded) }
onCellClicked = { onToggleExpanded(!expanded) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun AllProviders(
private fun LazyItemScope.AllProviders(
state: RelayFilterState,
onAllProviderCheckChange: (isChecked: Boolean) -> Unit
) {
CheckboxCell(
title = stringResource(R.string.all_providers),
checked = state.isAllProvidersChecked,
onCheckedChange = { isChecked -> onAllProviderCheckChange(isChecked) }
onCheckedChange = { isChecked -> onAllProviderCheckChange(isChecked) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun Provider(
private fun LazyItemScope.Provider(
provider: Provider,
state: RelayFilterState,
onSelectedProvider: (checked: Boolean, provider: Provider) -> Unit
) {
CheckboxCell(
title = provider.providerId.value,
checked = provider in state.selectedProviders,
onCheckedChange = { checked -> onSelectedProvider(checked, provider) }
onCheckedChange = { checked -> onSelectedProvider(checked, provider) },
modifier = Modifier.animateItem()
)
}

@Composable
private fun TopBar(onBackClick: () -> Unit) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onBackClick) {
Icon(
painter = painterResource(id = R.drawable.icon_back),
contentDescription = null,
tint = Color.Unspecified,
)
}
Text(
text = stringResource(R.string.filter),
modifier = Modifier.weight(1f).padding(end = Dimens.titleIconSize),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimary
)
}
}

@Composable
private fun BottomBar(
isApplyButtonEnabled: Boolean,
backgroundColor: Color,
onApplyClick: () -> Unit
) {
Box(
modifier =
Modifier.fillMaxWidth()
.background(color = backgroundColor)
.padding(top = Dimens.screenVerticalMargin),
contentAlignment = Alignment.BottomCenter
) {
ApplyButton(
onClick = onApplyClick,
isEnabled = isApplyButtonEnabled,
modifier =
Modifier.padding(
start = Dimens.sideMargin,
end = Dimens.sideMargin,
bottom = Dimens.screenVerticalMargin
),
)
}
}

private fun Ownership.stringResource(): Int =
when (this) {
Ownership.MullvadOwned -> R.string.mullvad_owned_only
Ownership.Rented -> R.string.rented_only
}

private object Keys {
const val OWNERSHIP_TITLE = "ownership_title"
const val OWNERSHIP_ALL = "ownership_all"
const val PROVIDERS_TITLE = "providers_title"
const val PROVIDERS_ALL = "providers_all"
}

0 comments on commit 95fec4f

Please sign in to comment.