Skip to content

Commit

Permalink
style: Improved timezone select dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
SuhasDissa committed Apr 16, 2024
1 parent 3ac87c3 commit 1645183
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package com.bnyro.clock.presentation.screens.clock.components

import android.R
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ListItem
import androidx.compose.material3.OutlinedTextField
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBackIos
import androidx.compose.material.icons.outlined.Circle
import androidx.compose.material.icons.rounded.CheckCircle
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
Expand All @@ -23,10 +36,17 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.bnyro.clock.presentation.components.DialogButton
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.bnyro.clock.domain.model.TimeZone
import com.bnyro.clock.presentation.screens.clock.model.ClockModel
import com.bnyro.clock.util.TimeHelper

@Composable
fun TimeZoneSelectDialog(
Expand All @@ -37,67 +57,162 @@ fun TimeZoneSelectDialog(
selectedTimeZones.toMutableStateList()
}

AlertDialog(onDismissRequest = onDismissRequest, confirmButton = {
DialogButton(label = R.string.ok) {
clockModel.setTimeZones(newTimeZones)
onDismissRequest.invoke()
}
}, dismissButton = {
DialogButton(label = R.string.cancel) {
onDismissRequest.invoke()
}
}, title = {
Text(stringResource(com.bnyro.clock.R.string.timezones))
}, text = {
var searchQuery by remember {
mutableStateOf("")
Dialog(
onDismissRequest = onDismissRequest,
properties = remember { DialogProperties(usePlatformDefaultWidth = false) }) {
Surface(color = MaterialTheme.colorScheme.surface) {
var searchQuery by remember {
mutableStateOf("")
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 8.dp)
) {
var filteredZones by remember { mutableStateOf(clockModel.timeZones) }
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp),
value = searchQuery,
onValueChange = {
searchQuery = it
val lowerQuery = searchQuery.lowercase()
filteredZones = clockModel.timeZones.filter {
it.countryName.lowercase()
.contains(lowerQuery) || it.zoneName.lowercase()
.contains(lowerQuery)
}
},
placeholder = { Text("Search Country/TimeZone") },
colors = TextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.secondaryContainer,
unfocusedContainerColor = MaterialTheme.colorScheme.secondaryContainer,
focusedTextColor = MaterialTheme.colorScheme.onSecondaryContainer,
unfocusedTextColor = MaterialTheme.colorScheme.onSecondaryContainer,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(50),
leadingIcon = {
IconButton(onClick = onDismissRequest) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBackIos,
contentDescription = null
)
}
}
)

LazyColumn(Modifier.weight(1f), contentPadding = PaddingValues(horizontal = 8.dp)) {
items(filteredZones) {
TimeZoneCard(
it,
selected = newTimeZones.contains(it),
onClick = { newCheckedState ->
if (!newCheckedState) {
newTimeZones.remove(it)
} else {
newTimeZones.add(it)
}
})
}
}
Row(
Modifier.align(Alignment.End),
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
OutlinedButton(onClick = { onDismissRequest.invoke() }) {
Text(text = stringResource(id = android.R.string.cancel))
}
Button(onClick = {
clockModel.setTimeZones(newTimeZones)
onDismissRequest.invoke()
}) {
Text(text = stringResource(id = android.R.string.ok))
}
}
}
}
}
}

Column(
@Composable
private fun TimeZoneCard(
timeZone: TimeZone,
selected: Boolean,
onClick: (Boolean) -> Unit
) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 6.dp)
.clip(RoundedCornerShape(20.dp))
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp))
.clickable {
onClick.invoke(!selected)
}
) {
Row(
modifier = Modifier
.heightIn(300.dp, 450.dp)
.fillMaxSize()
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
OutlinedTextField(modifier = Modifier.padding(vertical = 10.dp),
value = searchQuery,
onValueChange = { searchQuery = it },
label = { Text(stringResource(com.bnyro.clock.R.string.search)) })

LazyColumn {
val lowerQuery = searchQuery.lowercase()
val filteredZones = clockModel.timeZones.filter {
it.countryName.lowercase().contains(lowerQuery) || it.zoneName.lowercase()
.contains(lowerQuery)
}

items(filteredZones) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
ListItem(headlineContent = {
Text(text = it.zoneName)
}, supportingContent = {
Text(
text = it.countryName
)
}, leadingContent = {
Checkbox(checked = newTimeZones.contains(it),
onCheckedChange = { newCheckedState ->
if (!newCheckedState) {
newTimeZones.remove(it)
} else {
newTimeZones.add(it)
}
})
}, trailingContent = {
Text((it.offset.toFloat() / 1000 / 3600).toString())
})
}
IconButton(onClick = {
onClick.invoke(!selected)
}) {
if (selected) {
Icon(
imageVector = Icons.Rounded.CheckCircle,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
} else {
Icon(
imageVector = Icons.Outlined.Circle,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary
)
}
}
Column(Modifier.weight(1f)) {
Text(
text = timeZone.zoneName,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = timeZone.countryName,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Text(
modifier = Modifier.padding(
horizontal = 16.dp, vertical = 8.dp
),
text = TimeHelper.formatGMTTimeDifference(timeZone.offset.toFloat() / 1000 / 3600),
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.titleMedium
)
}
})
}
}

@Composable
@Preview
private fun TimeZoneCardPreview() {
TimeZoneCard(timeZone = TimeZone(
key = "America/New_York,New_York,United States",
zoneName = "New_York",
countryName = "United States",
offset = -14400000,
zoneId = "America/New_York"
), selected = true, onClick = {})
}
16 changes: 16 additions & 0 deletions app/src/main/java/com/bnyro/clock/util/TimeHelper.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bnyro.clock.util

import android.annotation.SuppressLint
import android.content.Context
import com.bnyro.clock.R
import com.bnyro.clock.domain.model.CountryTimezone
Expand Down Expand Up @@ -56,6 +57,21 @@ object TimeHelper {
return timeFormatter.format(time)
}

@SuppressLint("DefaultLocale")
fun formatGMTTimeDifference(timeDiff: Float): String {
val prefix = if (timeDiff >= 0) "+" else "-"
val hours = abs(timeDiff.toInt())
val minutes = (timeDiff * 60f % 60).toInt()

return if (minutes == 0) {
"GMT $prefix$hours"
} else {
val formattedMinutes = String.format("%02d", minutes)
"GMT $prefix$hours:$formattedMinutes"
}

}

fun millisToFormatted(millis: Long): String {
val timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
val localTime = LocalTime.of(
Expand Down

0 comments on commit 1645183

Please sign in to comment.