mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Resize & reshape buttons (#380)
Adds a new button implementation which allows for icon buttons to be circular. Overall the buttons are reduced in size. Also, the playback controls are are reduced in size, buttons are smaller and vertical padding is reduced. 
This commit is contained in:
parent
ac10517028
commit
793f9802e4
19 changed files with 492 additions and 206 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
package com.github.damontecres.wholphin.ui
|
package com.github.damontecres.wholphin.ui
|
||||||
|
|
||||||
import android.content.res.Configuration.UI_MODE_TYPE_TELEVISION
|
import android.content.res.Configuration.UI_MODE_TYPE_TELEVISION
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.Font
|
import androidx.compose.ui.text.font.Font
|
||||||
|
|
@ -66,14 +65,6 @@ val SlimItemFields =
|
||||||
ItemFields.SORT_NAME,
|
ItemFields.SORT_NAME,
|
||||||
)
|
)
|
||||||
|
|
||||||
val DefaultButtonPadding =
|
|
||||||
PaddingValues(
|
|
||||||
start = 12.dp / 2,
|
|
||||||
top = 10.dp / 2,
|
|
||||||
end = 16.dp / 2,
|
|
||||||
bottom = 10.dp / 2,
|
|
||||||
)
|
|
||||||
|
|
||||||
object Cards {
|
object Cards {
|
||||||
val height2x3 = 172.dp
|
val height2x3 = 172.dp
|
||||||
val playedPercentHeight = 6.dp
|
val playedPercentHeight = 6.dp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
// This file was inspired by related Button source files from
|
||||||
|
// https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/tv/tv-material/src/main/java/androidx/tv/material3
|
||||||
|
|
||||||
|
package com.github.damontecres.wholphin.ui.components
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.NonRestartableComposable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.semantics.role
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.tv.material3.Border
|
||||||
|
import androidx.tv.material3.ClickableSurfaceBorder
|
||||||
|
import androidx.tv.material3.ClickableSurfaceColors
|
||||||
|
import androidx.tv.material3.ClickableSurfaceDefaults
|
||||||
|
import androidx.tv.material3.ClickableSurfaceGlow
|
||||||
|
import androidx.tv.material3.ClickableSurfaceScale
|
||||||
|
import androidx.tv.material3.ClickableSurfaceShape
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import androidx.tv.material3.ProvideTextStyle
|
||||||
|
import androidx.tv.material3.Surface
|
||||||
|
import androidx.tv.material3.Text
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Button(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
scale: ClickableSurfaceScale = ClickableSurfaceDefaults.scale(),
|
||||||
|
glow: ClickableSurfaceGlow = ClickableSurfaceDefaults.glow(),
|
||||||
|
shape: ClickableSurfaceShape =
|
||||||
|
ClickableSurfaceDefaults.shape(
|
||||||
|
shape = CircleShape,
|
||||||
|
),
|
||||||
|
colors: ClickableSurfaceColors =
|
||||||
|
ClickableSurfaceDefaults.colors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.8f),
|
||||||
|
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
|
||||||
|
focusedContainerColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
focusedContentColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
|
pressedContainerColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
pressedContentColor = MaterialTheme.colorScheme.inverseOnSurface,
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f),
|
||||||
|
disabledContentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f),
|
||||||
|
),
|
||||||
|
tonalElevation: Dp = 0.dp,
|
||||||
|
border: ClickableSurfaceBorder =
|
||||||
|
ClickableSurfaceDefaults.border(
|
||||||
|
border = Border.None,
|
||||||
|
focusedBorder = Border.None,
|
||||||
|
pressedBorder = Border.None,
|
||||||
|
disabledBorder = Border.None,
|
||||||
|
focusedDisabledBorder =
|
||||||
|
Border(
|
||||||
|
border =
|
||||||
|
BorderStroke(
|
||||||
|
width = 1.5.dp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f),
|
||||||
|
),
|
||||||
|
shape = CircleShape,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentPadding: PaddingValues = DefaultButtonPadding,
|
||||||
|
contentHeight: Dp = MinButtonSize,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = modifier.semantics { role = Role.Button },
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
enabled = enabled,
|
||||||
|
scale = scale,
|
||||||
|
glow = glow,
|
||||||
|
shape = shape,
|
||||||
|
colors = colors,
|
||||||
|
tonalElevation = tonalElevation,
|
||||||
|
border = border,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
) {
|
||||||
|
ProvideTextStyle(value = MaterialTheme.typography.labelLarge) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(contentPadding)
|
||||||
|
.height(contentHeight),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@NonRestartableComposable
|
||||||
|
fun TextButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
contentPadding: PaddingValues =
|
||||||
|
PaddingValues(
|
||||||
|
start = 8.dp,
|
||||||
|
top = 4.dp,
|
||||||
|
end = 8.dp,
|
||||||
|
bottom = 4.dp,
|
||||||
|
),
|
||||||
|
contentHeight: Dp = 32.dp,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
content: @Composable RowScope.() -> Unit,
|
||||||
|
) = Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
enabled = enabled,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
contentHeight = contentHeight,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TextButton(
|
||||||
|
@StringRes stringRes: Int,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onLongClick: (() -> Unit)? = null,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
contentPadding: PaddingValues =
|
||||||
|
PaddingValues(
|
||||||
|
start = 8.dp,
|
||||||
|
top = 4.dp,
|
||||||
|
end = 8.dp,
|
||||||
|
bottom = 4.dp,
|
||||||
|
),
|
||||||
|
contentHeight: Dp = 32.dp,
|
||||||
|
interactionSource: MutableInteractionSource? = null,
|
||||||
|
) = Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
enabled = enabled,
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
contentHeight = contentHeight,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
content = {
|
||||||
|
Text(text = stringResource(stringRes))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
val DefaultButtonPadding =
|
||||||
|
PaddingValues(
|
||||||
|
start = 4.dp,
|
||||||
|
top = 4.dp,
|
||||||
|
end = 4.dp,
|
||||||
|
bottom = 4.dp,
|
||||||
|
)
|
||||||
|
|
@ -45,7 +45,6 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.ListItem
|
import androidx.tv.material3.ListItem
|
||||||
import androidx.tv.material3.LocalContentColor
|
import androidx.tv.material3.LocalContentColor
|
||||||
|
|
@ -443,20 +442,14 @@ fun ConfirmDialogContent(
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.cancel,
|
||||||
onClick = onCancel,
|
onClick = onCancel,
|
||||||
) {
|
)
|
||||||
Text(
|
TextButton(
|
||||||
text = stringResource(R.string.cancel),
|
stringRes = R.string.confirm,
|
||||||
)
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = onConfirm,
|
onClick = onConfirm,
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.confirm),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
|
|
@ -56,15 +55,12 @@ fun ErrorMessage(
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.send_app_logs,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.sendLogs()
|
viewModel.sendLogs()
|
||||||
},
|
},
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.send_app_logs),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
message?.let {
|
message?.let {
|
||||||
Text(
|
Text(
|
||||||
text = it,
|
text = it,
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,13 @@ import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.requiredSizeIn
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
|
@ -18,17 +22,16 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalResources
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.DpOffset
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
|
|
@ -46,8 +49,9 @@ import com.github.damontecres.wholphin.data.filter.PlayedFilter
|
||||||
import com.github.damontecres.wholphin.data.filter.VideoTypeFilter
|
import com.github.damontecres.wholphin.data.filter.VideoTypeFilter
|
||||||
import com.github.damontecres.wholphin.data.filter.YearFilter
|
import com.github.damontecres.wholphin.data.filter.YearFilter
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.ui.DefaultButtonPadding
|
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
@ -353,35 +357,64 @@ fun ExpandableFilterButton(
|
||||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
iconColor: Color = Color.Unspecified,
|
iconColor: Color = Color.Unspecified,
|
||||||
) {
|
) {
|
||||||
val resources = LocalResources.current
|
|
||||||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier.heightIn(min = 40.dp),
|
modifier =
|
||||||
contentPadding = DefaultButtonPadding,
|
modifier.requiredSizeIn(
|
||||||
|
minWidth = MinButtonSize,
|
||||||
|
minHeight = MinButtonSize,
|
||||||
|
maxHeight = MinButtonSize,
|
||||||
|
),
|
||||||
|
contentPadding = PaddingValues(0.dp), // DefaultButtonPadding,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
) {
|
) {
|
||||||
Text(
|
Box(
|
||||||
text = stringResource(R.string.fa_filter),
|
modifier =
|
||||||
style = MaterialTheme.typography.titleSmall,
|
Modifier
|
||||||
color = iconColor,
|
.fillMaxHeight()
|
||||||
fontSize = 16.sp,
|
.widthIn(min = if (filterCount > 0 || isFocused) (MinButtonSize - 12.dp) else MinButtonSize),
|
||||||
fontFamily = FontAwesome,
|
) {
|
||||||
textAlign = TextAlign.Center,
|
Text(
|
||||||
modifier = Modifier,
|
text = stringResource(R.string.fa_filter),
|
||||||
)
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = iconColor,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontFamily = FontAwesome,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
)
|
||||||
|
}
|
||||||
AnimatedVisibility(filterCount > 0) {
|
AnimatedVisibility(filterCount > 0) {
|
||||||
Text(
|
Text(
|
||||||
text = filterCount.toString(),
|
text = filterCount.toString(),
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
modifier = Modifier.padding(end = 4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimatedVisibility(isFocused) {
|
AnimatedVisibility(isFocused) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.filter),
|
text = stringResource(R.string.filter),
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
modifier = Modifier.padding(start = 4.dp, end = 4.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreviewTvSpec
|
||||||
|
@Composable
|
||||||
|
private fun ExpandableFilterButtonPreview() {
|
||||||
|
WholphinTheme {
|
||||||
|
Column {
|
||||||
|
ExpandableFilterButton(
|
||||||
|
filterCount = 2,
|
||||||
|
onClick = {},
|
||||||
|
)
|
||||||
|
|
||||||
|
ExpandableFilterButton(
|
||||||
|
filterCount = 0,
|
||||||
|
onClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,14 @@ import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.requiredSizeIn
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
|
@ -18,6 +22,7 @@ import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.FocusState
|
import androidx.compose.ui.focus.FocusState
|
||||||
|
|
@ -31,16 +36,18 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.DefaultButtonPadding
|
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
|
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
|
import com.github.damontecres.wholphin.ui.theme.PreviewInteractionSource
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
|
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||||
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
@ -77,7 +84,8 @@ fun ExpandablePlayButtons(
|
||||||
playOnClick,
|
playOnClick,
|
||||||
Modifier
|
Modifier
|
||||||
.onFocusChanged(buttonOnFocusChanged)
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
.focusRequester(firstFocus),
|
.focusRequester(firstFocus)
|
||||||
|
.animateItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item("restart") {
|
item("restart") {
|
||||||
|
|
@ -86,7 +94,9 @@ fun ExpandablePlayButtons(
|
||||||
Duration.ZERO,
|
Duration.ZERO,
|
||||||
Icons.Default.Refresh,
|
Icons.Default.Refresh,
|
||||||
playOnClick,
|
playOnClick,
|
||||||
Modifier.onFocusChanged(buttonOnFocusChanged),
|
Modifier
|
||||||
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
|
.animateItem(),
|
||||||
mirrorIcon = true,
|
mirrorIcon = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +109,8 @@ fun ExpandablePlayButtons(
|
||||||
playOnClick,
|
playOnClick,
|
||||||
Modifier
|
Modifier
|
||||||
.onFocusChanged(buttonOnFocusChanged)
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
.focusRequester(firstFocus),
|
.focusRequester(firstFocus)
|
||||||
|
.animateItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +121,10 @@ fun ExpandablePlayButtons(
|
||||||
title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
title = if (watched) R.string.mark_unwatched else R.string.mark_watched,
|
||||||
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
iconStringRes = if (watched) R.string.fa_eye else R.string.fa_eye_slash,
|
||||||
onClick = watchOnClick,
|
onClick = watchOnClick,
|
||||||
modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
|
.animateItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +135,10 @@ fun ExpandablePlayButtons(
|
||||||
iconStringRes = R.string.fa_heart,
|
iconStringRes = R.string.fa_heart,
|
||||||
onClick = favoriteOnClick,
|
onClick = favoriteOnClick,
|
||||||
iconColor = if (favorite) Color.Red else Color.Unspecified,
|
iconColor = if (favorite) Color.Red else Color.Unspecified,
|
||||||
modifier = Modifier.onFocusChanged(buttonOnFocusChanged),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
|
.animateItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,12 +149,16 @@ fun ExpandablePlayButtons(
|
||||||
Duration.ZERO,
|
Duration.ZERO,
|
||||||
Icons.Default.MoreVert,
|
Icons.Default.MoreVert,
|
||||||
{ moreOnClick.invoke() },
|
{ moreOnClick.invoke() },
|
||||||
Modifier.onFocusChanged(buttonOnFocusChanged),
|
Modifier
|
||||||
|
.onFocusChanged(buttonOnFocusChanged)
|
||||||
|
.animateItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val MinButtonSize = 40.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An icon button typically used in a row for playing media
|
* An icon button typically used in a row for playing media
|
||||||
*
|
*
|
||||||
|
|
@ -156,20 +177,36 @@ fun ExpandablePlayButton(
|
||||||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||||
Button(
|
Button(
|
||||||
onClick = { onClick.invoke(resume) },
|
onClick = { onClick.invoke(resume) },
|
||||||
modifier = modifier,
|
modifier =
|
||||||
|
modifier.requiredSizeIn(
|
||||||
|
minWidth = MinButtonSize,
|
||||||
|
minHeight = MinButtonSize,
|
||||||
|
maxHeight = MinButtonSize,
|
||||||
|
),
|
||||||
contentPadding = DefaultButtonPadding,
|
contentPadding = DefaultButtonPadding,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Box(
|
||||||
imageVector = icon,
|
modifier =
|
||||||
contentDescription = null,
|
Modifier
|
||||||
modifier = Modifier.ifElse(mirrorIcon, Modifier.graphicsLayer { scaleX = -1f }),
|
.padding(start = 2.dp, top = 2.dp)
|
||||||
)
|
.height(MinButtonSize),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.size(28.dp)
|
||||||
|
.ifElse(mirrorIcon, Modifier.graphicsLayer { scaleX = -1f }),
|
||||||
|
)
|
||||||
|
}
|
||||||
AnimatedVisibility(isFocused) {
|
AnimatedVisibility(isFocused) {
|
||||||
Spacer(Modifier.size(8.dp))
|
Spacer(Modifier.size(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(title),
|
text = stringResource(title),
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
modifier = Modifier.padding(end = 4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -190,24 +227,35 @@ fun ExpandableFaButton(
|
||||||
val isFocused = interactionSource.collectIsFocusedAsState().value
|
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = modifier.heightIn(min = 40.dp),
|
modifier =
|
||||||
|
modifier.requiredSizeIn(
|
||||||
|
minWidth = MinButtonSize,
|
||||||
|
minHeight = MinButtonSize,
|
||||||
|
maxHeight = MinButtonSize,
|
||||||
|
),
|
||||||
contentPadding = DefaultButtonPadding,
|
contentPadding = DefaultButtonPadding,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
) {
|
) {
|
||||||
Text(
|
Box(
|
||||||
text = stringResource(iconStringRes),
|
modifier =
|
||||||
style = MaterialTheme.typography.titleSmall,
|
Modifier
|
||||||
color = iconColor,
|
.size(32.dp),
|
||||||
fontSize = 16.sp,
|
) {
|
||||||
fontFamily = FontAwesome,
|
Text(
|
||||||
textAlign = TextAlign.Center,
|
text = stringResource(iconStringRes),
|
||||||
modifier = Modifier,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
)
|
color = iconColor,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontFamily = FontAwesome,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
)
|
||||||
|
}
|
||||||
AnimatedVisibility(isFocused) {
|
AnimatedVisibility(isFocused) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(title),
|
text = stringResource(title),
|
||||||
style = MaterialTheme.typography.titleSmall,
|
style = MaterialTheme.typography.titleSmall,
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
modifier = Modifier.padding(end = 4.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,3 +278,44 @@ private fun ExpandablePlayButtonsPreview() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreviewTvSpec
|
||||||
|
@Composable
|
||||||
|
private fun ViewOptionsPreview() {
|
||||||
|
val source = remember { PreviewInteractionSource() }
|
||||||
|
WholphinTheme {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
) {
|
||||||
|
ExpandablePlayButton(
|
||||||
|
title = R.string.play,
|
||||||
|
resume = Duration.ZERO,
|
||||||
|
icon = Icons.Default.PlayArrow,
|
||||||
|
onClick = {},
|
||||||
|
interactionSource = source,
|
||||||
|
)
|
||||||
|
ExpandableFaButton(
|
||||||
|
title = R.string.play,
|
||||||
|
iconStringRes = R.string.fa_eye,
|
||||||
|
onClick = {},
|
||||||
|
modifier = Modifier,
|
||||||
|
interactionSource = source,
|
||||||
|
)
|
||||||
|
|
||||||
|
Row {
|
||||||
|
ExpandableFaButton(
|
||||||
|
title = R.string.mark_unwatched,
|
||||||
|
iconStringRes = R.string.fa_eye,
|
||||||
|
onClick = {},
|
||||||
|
modifier = Modifier,
|
||||||
|
)
|
||||||
|
SortByButton(
|
||||||
|
sortOptions = listOf(),
|
||||||
|
current = SortAndDirection(ItemSortBy.DEFAULT, SortOrder.ASCENDING),
|
||||||
|
onSortChange = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,14 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.buildAnnotatedString
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||||
import com.github.damontecres.wholphin.ui.data.flip
|
import com.github.damontecres.wholphin.ui.data.flip
|
||||||
import com.github.damontecres.wholphin.ui.data.getStringRes
|
import com.github.damontecres.wholphin.ui.data.getStringRes
|
||||||
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ fun SortByButton(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
Button(
|
TextButton(
|
||||||
onClick = { sortByDropDown = true },
|
onClick = { sortByDropDown = true },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onSortChange.invoke(current.flip())
|
onSortChange.invoke(current.flip())
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
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.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
|
@ -45,9 +46,9 @@ import androidx.compose.ui.input.key.type
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.LocalContentColor
|
import androidx.tv.material3.LocalContentColor
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
|
|
@ -56,6 +57,8 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.ui.AppColors
|
import com.github.damontecres.wholphin.ui.AppColors
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.playback.isBackwardButton
|
import com.github.damontecres.wholphin.ui.playback.isBackwardButton
|
||||||
import com.github.damontecres.wholphin.ui.playback.isForwardButton
|
import com.github.damontecres.wholphin.ui.playback.isForwardButton
|
||||||
|
|
@ -438,6 +441,7 @@ fun AlphabetButtons(
|
||||||
}
|
}
|
||||||
val focusRequesters = remember { List(letters.length) { FocusRequester() } }
|
val focusRequesters = remember { List(letters.length) { FocusRequester() } }
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
contentPadding = PaddingValues(4.dp),
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier =
|
modifier =
|
||||||
modifier.focusProperties {
|
modifier.focusProperties {
|
||||||
|
|
@ -452,7 +456,7 @@ fun AlphabetButtons(
|
||||||
) { index ->
|
) { index ->
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
Button(
|
TextButton(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.size(24.dp)
|
.size(24.dp)
|
||||||
|
|
@ -472,6 +476,8 @@ fun AlphabetButtons(
|
||||||
Text(
|
Text(
|
||||||
text = letters[index].toString(),
|
text = letters[index].toString(),
|
||||||
color = color,
|
color = color,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.ListItem
|
import androidx.tv.material3.ListItem
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
|
@ -47,6 +46,7 @@ import androidx.tv.material3.surfaceColorAtElevation
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.PlaylistInfo
|
import com.github.damontecres.wholphin.data.model.PlaylistInfo
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,16 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.ifElse
|
import com.github.damontecres.wholphin.ui.ifElse
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.seasonEpisode
|
import com.github.damontecres.wholphin.ui.seasonEpisode
|
||||||
|
|
@ -145,7 +146,7 @@ fun ProgramDialog(
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
if (now.isAfter(dto.startDate!!) && now.isBefore(dto.endDate!!)) {
|
if (now.isAfter(dto.startDate!!) && now.isBefore(dto.endDate!!)) {
|
||||||
Button(
|
TextButton(
|
||||||
onClick = onWatch,
|
onClick = onWatch,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -179,7 +180,7 @@ fun ProgramDialog(
|
||||||
) {
|
) {
|
||||||
if (dto.isSeries ?: false) {
|
if (dto.isSeries ?: false) {
|
||||||
item {
|
item {
|
||||||
Button(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (isSeriesRecording) {
|
if (isSeriesRecording) {
|
||||||
onCancelRecord.invoke(true)
|
onCancelRecord.invoke(true)
|
||||||
|
|
@ -221,7 +222,7 @@ fun ProgramDialog(
|
||||||
if (dto.endDate?.isAfter(LocalDateTime.now()) ?: true) {
|
if (dto.endDate?.isAfter(LocalDateTime.now()) ?: true) {
|
||||||
// Only show program specific recording button if it hasn't finished yet
|
// Only show program specific recording button if it hasn't finished yet
|
||||||
item {
|
item {
|
||||||
Button(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
onCancelRecord.invoke(false)
|
onCancelRecord.invoke(false)
|
||||||
|
|
|
||||||
|
|
@ -45,15 +45,13 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.compose.ui.window.DialogWindowProvider
|
import androidx.compose.ui.window.DialogWindowProvider
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.tv.material3.Button
|
import androidx.tv.material3.ClickableSurfaceDefaults
|
||||||
import androidx.tv.material3.ButtonDefaults
|
|
||||||
import androidx.tv.material3.Icon
|
import androidx.tv.material3.Icon
|
||||||
import androidx.tv.material3.ListItem
|
import androidx.tv.material3.ListItem
|
||||||
import androidx.tv.material3.LocalContentColor
|
import androidx.tv.material3.LocalContentColor
|
||||||
|
|
@ -62,10 +60,14 @@ import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.preferences.AppThemeColors
|
import com.github.damontecres.wholphin.preferences.AppThemeColors
|
||||||
import com.github.damontecres.wholphin.ui.AppColors
|
import com.github.damontecres.wholphin.ui.AppColors
|
||||||
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.seekBack
|
import com.github.damontecres.wholphin.ui.seekBack
|
||||||
import com.github.damontecres.wholphin.ui.seekForward
|
import com.github.damontecres.wholphin.ui.seekForward
|
||||||
import com.github.damontecres.wholphin.ui.skipStringRes
|
import com.github.damontecres.wholphin.ui.skipStringRes
|
||||||
import com.github.damontecres.wholphin.ui.theme.LocalTheme
|
import com.github.damontecres.wholphin.ui.theme.LocalTheme
|
||||||
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
@ -192,7 +194,8 @@ fun PlaybackControls(
|
||||||
modifier = Modifier.align(Alignment.CenterEnd),
|
modifier = Modifier.align(Alignment.CenterEnd),
|
||||||
) {
|
) {
|
||||||
currentSegment?.let { segment ->
|
currentSegment?.let { segment ->
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = segment.type.skipStringRes,
|
||||||
onClick = {
|
onClick = {
|
||||||
playerControls.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
playerControls.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
||||||
},
|
},
|
||||||
|
|
@ -200,11 +203,7 @@ fun PlaybackControls(
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.CenterVertically)
|
.align(Alignment.CenterVertically)
|
||||||
.padding(end = 32.dp),
|
.padding(end = 32.dp),
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(segment.type.skipStringRes),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
RightPlaybackButtons(
|
RightPlaybackButtons(
|
||||||
captionFocusRequester = captionFocusRequester,
|
captionFocusRequester = captionFocusRequester,
|
||||||
|
|
@ -286,7 +285,7 @@ fun SeekBar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val buttonSpacing = 4.dp
|
private val buttonSpacing = 12.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LeftPlaybackButtons(
|
fun LeftPlaybackButtons(
|
||||||
|
|
@ -435,9 +434,9 @@ fun PlaybackButton(
|
||||||
Button(
|
Button(
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
shape = ButtonDefaults.shape(CircleShape),
|
// shape = ButtonDefaults.shape(CircleShape),
|
||||||
colors =
|
colors =
|
||||||
ButtonDefaults.colors(
|
ClickableSurfaceDefaults.colors(
|
||||||
containerColor = AppColors.TransparentBlack25,
|
containerColor = AppColors.TransparentBlack25,
|
||||||
focusedContainerColor = selectedColor,
|
focusedContainerColor = selectedColor,
|
||||||
),
|
),
|
||||||
|
|
@ -445,8 +444,7 @@ fun PlaybackButton(
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
.padding(4.dp)
|
.size(36.dp, 36.dp)
|
||||||
.size(44.dp, 44.dp)
|
|
||||||
.onFocusChanged { onControllerInteraction.invoke() },
|
.onFocusChanged { onControllerInteraction.invoke() },
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|
@ -543,3 +541,17 @@ fun BottomDialog(
|
||||||
data class MoreButtonOptions(
|
data class MoreButtonOptions(
|
||||||
val options: Map<String, PlaybackAction>,
|
val options: Map<String, PlaybackAction>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@PreviewTvSpec
|
||||||
|
@Composable
|
||||||
|
private fun ButtonPreview() {
|
||||||
|
WholphinTheme {
|
||||||
|
Row {
|
||||||
|
PlaybackButton(
|
||||||
|
iconRes = R.drawable.baseline_play_arrow_24,
|
||||||
|
onClick = {},
|
||||||
|
onControllerInteraction = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,38 +144,72 @@ fun PlaybackOverlay(
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
Controller(
|
Column(
|
||||||
title = item?.title,
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
subtitle = item?.subtitleLong,
|
|
||||||
playerControls = playerControls,
|
|
||||||
controllerViewState = controllerViewState,
|
|
||||||
showPlay = showPlay,
|
|
||||||
showClock = showClock,
|
|
||||||
previousEnabled = previousEnabled,
|
|
||||||
nextEnabled = nextEnabled,
|
|
||||||
seekEnabled = seekEnabled,
|
|
||||||
seekBack = seekBack,
|
|
||||||
skipBackOnResume = skipBackOnResume,
|
|
||||||
seekForward = seekForward,
|
|
||||||
onPlaybackActionClick = onPlaybackActionClick,
|
|
||||||
onClickPlaybackDialogType = onClickPlaybackDialogType,
|
|
||||||
onSeekProgress = {
|
|
||||||
onSeekBarChange(it)
|
|
||||||
seekProgressMs = it
|
|
||||||
},
|
|
||||||
seekBarInteractionSource = seekBarInteractionSource,
|
|
||||||
nextState = nextState,
|
|
||||||
onNextStateFocus = {
|
|
||||||
nextState?.let { state = it }
|
|
||||||
},
|
|
||||||
currentSegment = currentSegment,
|
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
// Don't use key events because this control has vertical items so up/down is tough to manage
|
.padding(bottom = 8.dp)
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned {
|
||||||
controllerHeight = with(density) { it.size.height.toDp() }
|
controllerHeight = with(density) { it.size.height.toDp() }
|
||||||
},
|
},
|
||||||
)
|
) {
|
||||||
|
Controller(
|
||||||
|
title = item?.title,
|
||||||
|
subtitle = item?.subtitleLong,
|
||||||
|
playerControls = playerControls,
|
||||||
|
controllerViewState = controllerViewState,
|
||||||
|
showPlay = showPlay,
|
||||||
|
showClock = showClock,
|
||||||
|
previousEnabled = previousEnabled,
|
||||||
|
nextEnabled = nextEnabled,
|
||||||
|
seekEnabled = seekEnabled,
|
||||||
|
seekBack = seekBack,
|
||||||
|
skipBackOnResume = skipBackOnResume,
|
||||||
|
seekForward = seekForward,
|
||||||
|
onPlaybackActionClick = onPlaybackActionClick,
|
||||||
|
onClickPlaybackDialogType = onClickPlaybackDialogType,
|
||||||
|
onSeekProgress = {
|
||||||
|
onSeekBarChange(it)
|
||||||
|
seekProgressMs = it
|
||||||
|
},
|
||||||
|
seekBarInteractionSource = seekBarInteractionSource,
|
||||||
|
nextState = nextState,
|
||||||
|
onNextStateFocus = {
|
||||||
|
nextState?.let { state = it }
|
||||||
|
},
|
||||||
|
currentSegment = currentSegment,
|
||||||
|
modifier =
|
||||||
|
Modifier,
|
||||||
|
// Don't use key events because this control has vertical items so up/down is tough to manage
|
||||||
|
)
|
||||||
|
when (nextState) {
|
||||||
|
OverlayViewState.CHAPTERS ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.chapters),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(start = 16.dp, top = 0.dp)
|
||||||
|
.onFocusChanged {
|
||||||
|
if (it.isFocused) state = nextState
|
||||||
|
}.focusable(),
|
||||||
|
)
|
||||||
|
|
||||||
|
OverlayViewState.QUEUE ->
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.queue),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(start = 16.dp, top = 0.dp)
|
||||||
|
.onFocusChanged {
|
||||||
|
if (it.isFocused) state = nextState
|
||||||
|
}.focusable(),
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> Spacer(Modifier.height(32.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
state == OverlayViewState.CHAPTERS,
|
state == OverlayViewState.CHAPTERS,
|
||||||
|
|
@ -189,8 +223,8 @@ fun PlaybackOverlay(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
.padding(horizontal = 8.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(8.dp)
|
|
||||||
.onPreviewKeyEvent { e ->
|
.onPreviewKeyEvent { e ->
|
||||||
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
||||||
state = OverlayViewState.CONTROLLER
|
state = OverlayViewState.CONTROLLER
|
||||||
|
|
@ -247,7 +281,7 @@ fun PlaybackOverlay(
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(start = 16.dp, top = 16.dp)
|
.padding(bottom = 8.dp)
|
||||||
.onFocusChanged {
|
.onFocusChanged {
|
||||||
if (it.isFocused) state = OverlayViewState.QUEUE
|
if (it.isFocused) state = OverlayViewState.QUEUE
|
||||||
}.focusable(),
|
}.focusable(),
|
||||||
|
|
@ -269,8 +303,8 @@ fun PlaybackOverlay(
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
.onPreviewKeyEvent { e ->
|
.onPreviewKeyEvent { e ->
|
||||||
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
if (e.type == KeyEventType.KeyUp && isUp(e)) {
|
||||||
if (chapters.isNotEmpty()) {
|
if (chapters.isNotEmpty()) {
|
||||||
|
|
@ -291,7 +325,7 @@ fun PlaybackOverlay(
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
)
|
)
|
||||||
LazyRow(
|
LazyRow(
|
||||||
contentPadding = PaddingValues(16.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
@ -538,32 +572,5 @@ fun Controller(
|
||||||
captionFocusRequester = captionFocusRequester,
|
captionFocusRequester = captionFocusRequester,
|
||||||
settingsFocusRequester = settingsFocusRequester,
|
settingsFocusRequester = settingsFocusRequester,
|
||||||
)
|
)
|
||||||
when (nextState) {
|
|
||||||
OverlayViewState.CHAPTERS ->
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.chapters),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(start = 16.dp, top = 16.dp)
|
|
||||||
.onFocusChanged {
|
|
||||||
if (it.isFocused) onNextStateFocus.invoke()
|
|
||||||
}.focusable(),
|
|
||||||
)
|
|
||||||
|
|
||||||
OverlayViewState.QUEUE ->
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.queue),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(start = 16.dp, top = 16.dp)
|
|
||||||
.onFocusChanged {
|
|
||||||
if (it.isFocused) onNextStateFocus.invoke()
|
|
||||||
}.focusable(),
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> Spacer(Modifier.height(32.dp))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.input.key.onKeyEvent
|
import androidx.compose.ui.input.key.onKeyEvent
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.intl.Locale
|
import androidx.compose.ui.text.intl.Locale
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
|
@ -57,9 +56,7 @@ import androidx.media3.ui.compose.SURFACE_TYPE_SURFACE_VIEW
|
||||||
import androidx.media3.ui.compose.modifiers.resizeWithContentScale
|
import androidx.media3.ui.compose.modifiers.resizeWithContentScale
|
||||||
import androidx.media3.ui.compose.state.rememberPlayPauseButtonState
|
import androidx.media3.ui.compose.state.rememberPlayPauseButtonState
|
||||||
import androidx.media3.ui.compose.state.rememberPresentationState
|
import androidx.media3.ui.compose.state.rememberPresentationState
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.data.model.Playlist
|
import com.github.damontecres.wholphin.data.model.Playlist
|
||||||
|
|
@ -71,6 +68,7 @@ import com.github.damontecres.wholphin.ui.LocalImageUrlService
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.applyToMpv
|
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.applyToMpv
|
||||||
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.calculateEdgeSize
|
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings.calculateEdgeSize
|
||||||
|
|
@ -403,17 +401,14 @@ fun PlaybackPage(
|
||||||
delay(10.seconds)
|
delay(10.seconds)
|
||||||
segmentCancelled = true
|
segmentCancelled = true
|
||||||
}
|
}
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = segment.type.skipStringRes,
|
||||||
onClick = {
|
onClick = {
|
||||||
segmentCancelled = true
|
segmentCancelled = true
|
||||||
player.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
player.seekTo(segment.endTicks.ticks.inWholeMilliseconds)
|
||||||
},
|
},
|
||||||
modifier = Modifier.focusRequester(focusRequester),
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(segment.type.skipStringRes),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,13 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StringInputDialog(
|
fun StringInputDialog(
|
||||||
|
|
@ -95,22 +95,16 @@ fun StringInputDialog(
|
||||||
horizontalArrangement = Arrangement.SpaceAround,
|
horizontalArrangement = Arrangement.SpaceAround,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.cancel,
|
||||||
onClick = onDismissRequest,
|
onClick = onDismissRequest,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
) {
|
)
|
||||||
Text(
|
TextButton(
|
||||||
text = stringResource(R.string.cancel),
|
stringRes = R.string.save,
|
||||||
)
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = onDone,
|
onClick = onDone,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.save),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
|
|
@ -54,8 +53,10 @@ import com.github.damontecres.wholphin.services.Release
|
||||||
import com.github.damontecres.wholphin.services.UpdateChecker
|
import com.github.damontecres.wholphin.services.UpdateChecker
|
||||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||||
import com.github.damontecres.wholphin.ui.formatBytes
|
import com.github.damontecres.wholphin.ui.formatBytes
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
|
|
@ -299,20 +300,14 @@ fun InstallUpdatePageContent(
|
||||||
style = MaterialTheme.typography.titleLarge,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.download_and_update,
|
||||||
onClick = onInstallRelease,
|
onClick = onInstallRelease,
|
||||||
) {
|
)
|
||||||
Text(
|
TextButton(
|
||||||
text = stringResource(R.string.download_and_update),
|
stringRes = R.string.cancel,
|
||||||
)
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
onClick = onCancel,
|
onClick = onCancel,
|
||||||
) {
|
)
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.cancel),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,14 @@ import androidx.compose.ui.input.key.type
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||||
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
import com.github.damontecres.wholphin.ui.PreviewTvSpec
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
|
import com.github.damontecres.wholphin.ui.components.Button
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.playback.isEnterKey
|
import com.github.damontecres.wholphin.ui.playback.isEnterKey
|
||||||
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ fun PinEntry(
|
||||||
PinArrowRow(Modifier.align(Alignment.CenterHorizontally))
|
PinArrowRow(Modifier.align(Alignment.CenterHorizontally))
|
||||||
PinEntryDots(input.length, Modifier.align(Alignment.CenterHorizontally))
|
PinEntryDots(input.length, Modifier.align(Alignment.CenterHorizontally))
|
||||||
|
|
||||||
Button(
|
TextButton(
|
||||||
onClick = onClickServerAuth,
|
onClick = onClickServerAuth,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
|
|
@ -37,6 +36,7 @@ import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
@ -224,7 +224,7 @@ fun SwitchServerContent(
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
Button(
|
TextButton(
|
||||||
onClick = { submit.invoke() },
|
onClick = { submit.invoke() },
|
||||||
enabled = url.text.isNotNullOrBlank() && state == LoadingState.Pending,
|
enabled = url.text.isNotNullOrBlank() && state == LoadingState.Pending,
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.tv.material3.Button
|
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
import androidx.tv.material3.surfaceColorAtElevation
|
import androidx.tv.material3.surfaceColorAtElevation
|
||||||
|
|
@ -44,6 +43,7 @@ import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
import com.github.damontecres.wholphin.ui.components.EditTextBox
|
||||||
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.dimAndBlur
|
import com.github.damontecres.wholphin.ui.dimAndBlur
|
||||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
|
@ -203,16 +203,15 @@ fun SwitchUserContent(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
UserStateError(userState)
|
UserStateError(userState)
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.username_or_password,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.cancelQuickConnect()
|
viewModel.cancelQuickConnect()
|
||||||
viewModel.clearSwitchUserState()
|
viewModel.clearSwitchUserState()
|
||||||
useQuickConnect = false
|
useQuickConnect = false
|
||||||
},
|
},
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
) {
|
)
|
||||||
Text(text = "Use username/password")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val username = rememberTextFieldState()
|
val username = rememberTextFieldState()
|
||||||
val password = rememberTextFieldState()
|
val password = rememberTextFieldState()
|
||||||
|
|
@ -282,27 +281,25 @@ fun SwitchUserContent(
|
||||||
modifier = Modifier.focusRequester(passwordFocusRequester),
|
modifier = Modifier.focusRequester(passwordFocusRequester),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.login,
|
||||||
onClick = { onSubmit.invoke() },
|
onClick = { onSubmit.invoke() },
|
||||||
enabled = username.text.isNotNullOrBlank() && password.text.isNotNullOrBlank(),
|
enabled = username.text.isNotNullOrBlank() && password.text.isNotNullOrBlank(),
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
) {
|
)
|
||||||
Text("Login")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (loginAttempts > 2) {
|
if (loginAttempts > 2) {
|
||||||
Text(
|
Text(
|
||||||
text = "Trouble logging in?",
|
text = "Trouble logging in?",
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
)
|
)
|
||||||
Button(
|
TextButton(
|
||||||
|
stringRes = R.string.show_debug_info,
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.navigationManager.navigateTo(Destination.Debug)
|
viewModel.navigationManager.navigateTo(Destination.Debug)
|
||||||
},
|
},
|
||||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||||
) {
|
)
|
||||||
Text("Show debug info")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,8 @@
|
||||||
<string name="cast_and_crew"><![CDATA[Cast & Crew]]></string>
|
<string name="cast_and_crew"><![CDATA[Cast & Crew]]></string>
|
||||||
<string name="guest_stars">Guest stars</string>
|
<string name="guest_stars">Guest stars</string>
|
||||||
<string name="edge_size">Edge size</string>
|
<string name="edge_size">Edge size</string>
|
||||||
|
<string name="username_or_password">Use username/password</string>
|
||||||
|
<string name="login">Login</string>
|
||||||
|
|
||||||
|
|
||||||
<string-array name="theme_song_volume">
|
<string-array name="theme_song_volume">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue