create top level switch and palette view on settings page

This commit is contained in:
Owen LeJeune
2022-02-10 13:57:43 -05:00
parent 09982636b2
commit 620cbca62d
5 changed files with 240 additions and 27 deletions

View File

@@ -5,6 +5,8 @@ import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.rememberSplineBasedDecay
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
@@ -66,11 +68,13 @@ fun MyApp() {
floatingActionButton = {
SearchFab()
}
) {
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
NavigationRoutes(navController = navController)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View File

@@ -0,0 +1,15 @@
package com.owenlejeune.tvtime.api.tmdb
import com.owenlejeune.tvtime.api.tmdb.model.MediaItem
object TmdbUtils {
fun getFullPosterPath(posterPath: String?): String {
return "https://image.tmdb.org/t/p/original${posterPath}"
}
fun getFullPosterPath(mediaItem: MediaItem): String {
return getFullPosterPath(mediaItem.posterPath)
}
}

View File

@@ -3,11 +3,12 @@ package com.owenlejeune.tvtime.extensions
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.LazyGridScope
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.runtime.Composable
import androidx.paging.compose.LazyPagingItems
@OptIn(ExperimentalFoundationApi::class)
fun <T: Any> LazyGridScope.items(
fun <T: Any> LazyGridScope.lazyPagingItems(
lazyPagingItems: LazyPagingItems<T>,
itemContent: @Composable LazyItemScope.(value: T?) -> Unit
) {
@@ -17,7 +18,16 @@ fun <T: Any> LazyGridScope.items(
}
@OptIn(ExperimentalFoundationApi::class)
fun <T: Any> LazyGridScope.items(
fun <T: Any> LazyGridScope.listItems(
items: List<T>,
itemContent: @Composable (value: T) -> Unit
) {
items(items.size) { index ->
itemContent(items[index])
}
}
fun <T: Any> LazyListScope.listItems(
items: List<T>,
itemContent: @Composable (value: T) -> Unit
) {

View File

@@ -1,27 +1,41 @@
package com.owenlejeune.tvtime.ui.components
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.widget.Toast
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.Composable
import com.owenlejeune.tvtime.api.tmdb.model.MediaItem
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.GridCells
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.rememberImagePainter
import coil.transform.RoundedCornersTransformation
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.TmdbUtils
import com.owenlejeune.tvtime.api.tmdb.model.MediaItem
import com.owenlejeune.tvtime.extensions.dpToPx
import com.owenlejeune.tvtime.extensions.items
import com.owenlejeune.tvtime.extensions.listItems
@OptIn(ExperimentalFoundationApi::class)
@Composable
@@ -33,7 +47,7 @@ fun PosterGrid(fetchMedia: (MutableState<List<MediaItem>>) -> Unit) {
cells = GridCells.Fixed(count = 3),
contentPadding = PaddingValues(8.dp)
) {
items(mediaList.value) { item ->
listItems(mediaList.value) { item ->
PosterItem(mediaItem = item)
}
}
@@ -42,7 +56,7 @@ fun PosterGrid(fetchMedia: (MutableState<List<MediaItem>>) -> Unit) {
@Composable
fun PosterItem(mediaItem: MediaItem) {
val context = LocalContext.current
val poster = "https://image.tmdb.org/t/p/original${mediaItem.posterPath}"
val poster = TmdbUtils.getFullPosterPath(mediaItem)
Image(
painter = rememberImagePainter(
data = poster,
@@ -62,3 +76,171 @@ fun PosterItem(mediaItem: MediaItem) {
}
)
}
@Preview
@Composable
fun PosterItemPreview() {
PosterItem(mediaItem = object : MediaItem(
title = "Deadpool",
posterPath = "/fSRb7vyIP8rQpL0I47P3qUsEKX3.jpg"
){})
}
@Composable
fun TopLevelSwitch(
text: String,
checkedState: MutableState<Boolean> = remember { mutableStateOf(false) },
onCheckChanged: (Boolean) -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(12.dp),
shape = RoundedCornerShape(30.dp),
backgroundColor = when {
isSystemInDarkTheme() && checkedState.value -> MaterialTheme.colorScheme.primary
isSystemInDarkTheme() && !checkedState.value -> MaterialTheme.colorScheme.secondary
checkedState.value -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
}
) {
Row(
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = text,
color = when {
isSystemInDarkTheme() && checkedState.value -> MaterialTheme.colorScheme.onPrimary
isSystemInDarkTheme() && !checkedState.value -> MaterialTheme.colorScheme.onSecondary
checkedState.value -> MaterialTheme.colorScheme.onPrimaryContainer
else -> MaterialTheme.colorScheme.onSecondaryContainer
},
modifier = Modifier.padding(30.dp, 12.dp),
fontSize = 18.sp
)
CustomSwitch(
modifier = Modifier.padding(40.dp, 12.dp),
onCheckChanged = { isChecked ->
checkedState.value = isChecked
onCheckChanged(isChecked)
}
)
}
}
}
@Composable
fun CustomSwitch(
modifier: Modifier = Modifier,
switchState: MutableState<Boolean> = remember { mutableStateOf(false) },
onCheckChanged: (Boolean) -> Unit = {}
) {
val width = 30.dp
val height = 15.dp
val gapBetweenThumbAndTrackEdge = 2.dp
val thumbRadius = (height / 2) - gapBetweenThumbAndTrackEdge
val animatePosition = animateFloatAsState(
targetValue = if (switchState.value) {
with(LocalDensity.current) { (width - thumbRadius - gapBetweenThumbAndTrackEdge).toPx() }
} else {
with (LocalDensity.current) { (thumbRadius + gapBetweenThumbAndTrackEdge).toPx() }
}
)
val uncheckedTrackColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.surfaceVariant else MaterialTheme.colorScheme.outline
val uncheckedThumbColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.outline else MaterialTheme.colorScheme.surfaceVariant
val checkedTrackColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.outline else MaterialTheme.colorScheme.primary
val checkedThumbColor = if (isSystemInDarkTheme()) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.primaryContainer
Canvas(
modifier = modifier
.size(width = width, height = height)
.scale(scale = 2f)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
switchState.value = !switchState.value
onCheckChanged(switchState.value)
}
)
}
) {
drawRoundRect(
color = if (switchState.value) checkedTrackColor else uncheckedTrackColor,
cornerRadius = CornerRadius(x = 10.dp.toPx(), y = 10.dp.toPx())
)
drawCircle(
color = if (switchState.value) checkedThumbColor else uncheckedThumbColor,
radius = thumbRadius.toPx(),
center = Offset(
x = animatePosition.value,
y = size.height / 2
)
)
}
}
@Preview(name = "TopLevelSwitch", showBackground = true)
@Preview(name = "Dark TopLevelSwitch", showBackground = true, uiMode = UI_MODE_NIGHT_YES)
@Composable
fun TopLevelSwitchPreview() {
val context = LocalContext.current
TopLevelSwitch("This is a switch") { isChecked ->
Toast.makeText(context, "Switch changed to $isChecked", Toast.LENGTH_SHORT).show()
}
}
@Composable
fun PaletteView() {
class PaletteItem(val color: Color, val textColor: Color, val text: String)
val items = listOf(
PaletteItem(MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.onPrimary, "primary"),
PaletteItem(MaterialTheme.colorScheme.primaryContainer, MaterialTheme.colorScheme.onPrimaryContainer, "primary container"),
PaletteItem(MaterialTheme.colorScheme.inversePrimary, Color.Black, "inverse primary"),
PaletteItem(MaterialTheme.colorScheme.secondary, MaterialTheme.colorScheme.onSecondary, "secondary"),
PaletteItem(MaterialTheme.colorScheme.secondaryContainer, MaterialTheme.colorScheme.onSecondaryContainer, "secondary container"),
PaletteItem(MaterialTheme.colorScheme.tertiary, MaterialTheme.colorScheme.onTertiary, "tertiary"),
PaletteItem(MaterialTheme.colorScheme.tertiaryContainer, MaterialTheme.colorScheme.onTertiaryContainer, "tertiary container"),
PaletteItem(MaterialTheme.colorScheme.background, MaterialTheme.colorScheme.onBackground, "background"),
PaletteItem(MaterialTheme.colorScheme.surface, MaterialTheme.colorScheme.onSurface, "surface"),
PaletteItem(MaterialTheme.colorScheme.surfaceVariant, MaterialTheme.colorScheme.onSurfaceVariant, "surface variant"),
PaletteItem(MaterialTheme.colorScheme.inverseSurface, MaterialTheme.colorScheme.inverseOnSurface, "inverse surface"),
PaletteItem(MaterialTheme.colorScheme.error, MaterialTheme.colorScheme.onError, "error"),
PaletteItem(MaterialTheme.colorScheme.errorContainer, MaterialTheme.colorScheme.onErrorContainer, "primary"),
PaletteItem(MaterialTheme.colorScheme.outline, Color.Black, "outline")
)
LazyColumn(modifier = Modifier.fillMaxSize()) {
listItems(items) { item ->
PaletteItemView(
surfaceColor = item.color,
textColor = item.textColor,
text = item.text
)
}
}
}
@Composable
fun PaletteItemView(surfaceColor: Color, textColor: Color, text: String) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(12.dp),
shape = RoundedCornerShape(30.dp),
backgroundColor = surfaceColor
) {
Text(
text = text,
color = textColor,
modifier = Modifier.padding(30.dp, 12.dp)
)
}
}

View File

@@ -2,23 +2,25 @@ package com.owenlejeune.tvtime.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.owenlejeune.tvtime.ui.components.PaletteView
import com.owenlejeune.tvtime.ui.components.TopLevelSwitch
@Composable
fun SettingsTab() {
Column(
modifier = Modifier
.fillMaxSize()
.wrapContentSize(Alignment.Center)
) {
Text(
text = "Settings Tab",
color = MaterialTheme.colorScheme.onBackground
Column(modifier = Modifier.fillMaxSize()) {
val showPalette = remember { mutableStateOf(false) }
TopLevelSwitch(
text = "Show Palette",
onCheckChanged = { isChecked ->
showPalette.value = isChecked
}
)
if (showPalette.value) {
PaletteView()
}
}
}