mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Updates to TV guide UI (#263)
Reduces the size of the rows on the TV guide allowing for 7-8 rows to show. Also hide the tab row if scrolled down on the guide. Move the "watch live" button to be initially focused Channels are sorted with favorites appearing first Back button behavior is tweaked to move to start of row first, then top of the guide, then normal back behavior Closes #259 Related to #250
This commit is contained in:
parent
461fbe846c
commit
59fc88f395
5 changed files with 266 additions and 181 deletions
|
|
@ -135,6 +135,9 @@ fun CollectionFolderLiveTv(
|
|||
0 -> {
|
||||
TvGuideGrid(
|
||||
true,
|
||||
onRowPosition = {
|
||||
showHeader = it <= 0
|
||||
},
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.focusRequester(focusRequester),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
package com.github.damontecres.wholphin.ui.detail.livetv
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.contentColorFor
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import coil3.compose.AsyncImage
|
||||
|
||||
@Composable
|
||||
fun Program(
|
||||
program: TvProgram,
|
||||
focused: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val background =
|
||||
if (focused) {
|
||||
MaterialTheme.colorScheme.inverseSurface
|
||||
} else {
|
||||
program.category?.color
|
||||
?: MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)
|
||||
}
|
||||
val textColor = MaterialTheme.colorScheme.contentColorFor(background)
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.padding(2.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
background,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = program.name ?: program.id.toString(),
|
||||
color = textColor,
|
||||
fontSize = 16.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier,
|
||||
)
|
||||
listOfNotNull(
|
||||
program.seasonEpisode?.let { "S${it.season} E${it.episode}" },
|
||||
program.subtitle,
|
||||
).joinToString(" - ")
|
||||
.ifBlank { null }
|
||||
?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = textColor,
|
||||
fontSize = 14.sp,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
RecordingMarker(
|
||||
isRecording = program.isRecording,
|
||||
isSeriesRecording = program.isSeriesRecording,
|
||||
modifier = Modifier.align(Alignment.BottomEnd),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Channel(
|
||||
channel: TvChannel,
|
||||
channelIndex: Int,
|
||||
focused: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val background =
|
||||
if (focused) {
|
||||
MaterialTheme.colorScheme.inverseSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.surface
|
||||
}
|
||||
val textColor = MaterialTheme.colorScheme.contentColorFor(background)
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
background,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Text(
|
||||
text = channel.number ?: channel.name ?: channelIndex.toString(),
|
||||
color = textColor,
|
||||
modifier = Modifier,
|
||||
)
|
||||
AsyncImage(
|
||||
model = channel.imageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxHeight(.66f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.WholphinApplication
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.AppColors
|
||||
|
|
@ -56,6 +57,7 @@ class LiveTvViewModel
|
|||
@param:ApplicationContext private val context: Context,
|
||||
val api: ApiClient,
|
||||
val navigationManager: NavigationManager,
|
||||
private val serverRepository: ServerRepository,
|
||||
) : ViewModel() {
|
||||
val loading = MutableLiveData<LoadingState>(LoadingState.Pending)
|
||||
|
||||
|
|
@ -88,6 +90,9 @@ class LiveTvViewModel
|
|||
val channelData by api.liveTvApi.getLiveTvChannels(
|
||||
GetLiveTvChannelsRequest(
|
||||
startIndex = 0,
|
||||
userId = serverRepository.currentUser.value?.id,
|
||||
enableFavoriteSorting = true,
|
||||
addCurrentProgram = false,
|
||||
),
|
||||
)
|
||||
val channels =
|
||||
|
|
@ -103,8 +108,8 @@ class LiveTvViewModel
|
|||
Timber.d("Got ${channels.size} channels")
|
||||
channelsIdToIndex =
|
||||
channels.withIndex().associateBy({ it.value.id }, { it.index })
|
||||
// Initially, quickly load the first 10 channels (only ~6 are visible immediately), then below will load more
|
||||
// This makes the guide appear faster, and load more useable data in the background
|
||||
// Initially, quickly load the first 10 channels (only some are visible immediately), then below will load more
|
||||
// This makes the guide appear faster, and load more usable data in the background
|
||||
val initial = 10
|
||||
fetchPrograms(channels, 0..<initial.coerceAtMost(channels.size))
|
||||
|
||||
|
|
@ -212,9 +217,6 @@ class LiveTvViewModel
|
|||
}
|
||||
listing.add(p)
|
||||
} else if (index > 0) {
|
||||
if (channelId == UUID.fromString("638232b7-7ef7-7e98-903d-2249ee3fd2cd")) {
|
||||
Timber.v("Found ")
|
||||
}
|
||||
var previous = listing.last()
|
||||
while (previous.endHours < p.startHours) {
|
||||
// Fill gaps between programs
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
|
|
@ -20,6 +21,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -35,6 +37,7 @@ import com.github.damontecres.wholphin.R
|
|||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.wholphin.ui.seasonEpisode
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -141,86 +144,10 @@ fun ProgramDialog(
|
|||
.padding(top = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
if (canRecord) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
if (dto.isSeries ?: false) {
|
||||
Button(
|
||||
onClick = {
|
||||
if (isSeriesRecording) {
|
||||
onCancelRecord.invoke(true)
|
||||
} else {
|
||||
onRecord.invoke(true)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isSeriesRecording) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color.Red,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text =
|
||||
if (isSeriesRecording) {
|
||||
stringResource(
|
||||
R.string.cancel_series_recording,
|
||||
)
|
||||
} else {
|
||||
stringResource(R.string.record_series)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dto.endDate?.isAfter(LocalDateTime.now()) ?: true) {
|
||||
// Only show program specific recording button if it hasn't finished yet
|
||||
Button(
|
||||
onClick = {
|
||||
if (isRecording) {
|
||||
onCancelRecord.invoke(false)
|
||||
} else {
|
||||
onRecord.invoke(false)
|
||||
}
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isRecording) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color.Red,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text =
|
||||
if (isRecording) {
|
||||
stringResource(
|
||||
R.string.cancel_recording,
|
||||
)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.record_program,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (now.isAfter(dto.startDate!!) && now.isBefore(dto.endDate!!)) {
|
||||
Button(
|
||||
onClick = onWatch,
|
||||
modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
|
|
@ -237,6 +164,111 @@ fun ProgramDialog(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (canRecord) {
|
||||
val recordFocusRequester = remember { FocusRequester() }
|
||||
LazyRow(
|
||||
horizontalArrangement =
|
||||
Arrangement.spacedBy(
|
||||
16.dp,
|
||||
Alignment.CenterHorizontally,
|
||||
),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRestorer(recordFocusRequester),
|
||||
) {
|
||||
if (dto.isSeries ?: false) {
|
||||
item {
|
||||
Button(
|
||||
onClick = {
|
||||
if (isSeriesRecording) {
|
||||
onCancelRecord.invoke(true)
|
||||
} else {
|
||||
onRecord.invoke(true)
|
||||
}
|
||||
},
|
||||
modifier =
|
||||
Modifier.focusRequester(recordFocusRequester),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement =
|
||||
Arrangement.spacedBy(
|
||||
4.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isSeriesRecording) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color.Red,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text =
|
||||
if (isSeriesRecording) {
|
||||
stringResource(
|
||||
R.string.cancel_series_recording,
|
||||
)
|
||||
} else {
|
||||
stringResource(R.string.record_series)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dto.endDate?.isAfter(LocalDateTime.now()) ?: true) {
|
||||
// Only show program specific recording button if it hasn't finished yet
|
||||
item {
|
||||
Button(
|
||||
onClick = {
|
||||
if (isRecording) {
|
||||
onCancelRecord.invoke(false)
|
||||
} else {
|
||||
onRecord.invoke(false)
|
||||
}
|
||||
},
|
||||
modifier =
|
||||
Modifier.ifElse(
|
||||
!(dto.isSeries ?: false),
|
||||
Modifier.focusRequester(
|
||||
recordFocusRequester,
|
||||
),
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement =
|
||||
Arrangement.spacedBy(
|
||||
4.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isRecording) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = null,
|
||||
tint = Color.Red,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text =
|
||||
if (isRecording) {
|
||||
stringResource(
|
||||
R.string.cancel_recording,
|
||||
)
|
||||
} else {
|
||||
stringResource(
|
||||
R.string.record_program,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,12 @@ import android.text.format.DateUtils
|
|||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -42,14 +38,10 @@ import androidx.tv.material3.MaterialTheme
|
|||
import androidx.tv.material3.Surface
|
||||
import androidx.tv.material3.SurfaceDefaults
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.contentColorFor
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import coil3.compose.AsyncImage
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
|
|
@ -69,6 +61,7 @@ import kotlin.math.abs
|
|||
@Composable
|
||||
fun TvGuideGrid(
|
||||
requestFocusAfterLoading: Boolean,
|
||||
onRowPosition: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: LiveTvViewModel = hiltViewModel(),
|
||||
) {
|
||||
|
|
@ -101,7 +94,7 @@ fun TvGuideGrid(
|
|||
}
|
||||
}
|
||||
Column(modifier = modifier) {
|
||||
TvGuideGrid(
|
||||
TvGuideGridContent(
|
||||
loading = state is LoadingState.Loading,
|
||||
channels = channels,
|
||||
programs = programs,
|
||||
|
|
@ -115,7 +108,10 @@ fun TvGuideGrid(
|
|||
),
|
||||
)
|
||||
},
|
||||
onFocus = viewModel::onFocusChannel,
|
||||
onFocus = {
|
||||
onRowPosition.invoke(it.row)
|
||||
viewModel.onFocusChannel(it)
|
||||
},
|
||||
onClickProgram = { index, program ->
|
||||
if (program.isFake) {
|
||||
val now = LocalDateTime.now()
|
||||
|
|
@ -186,7 +182,7 @@ fun TvGuideGrid(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun TvGuideGrid(
|
||||
fun TvGuideGridContent(
|
||||
loading: Boolean,
|
||||
channels: List<TvChannel>,
|
||||
programs: FetchedPrograms,
|
||||
|
|
@ -207,7 +203,7 @@ fun TvGuideGrid(
|
|||
val focusedProgramIndex =
|
||||
remember(programs.range, focusedItem) {
|
||||
focusedItem.let { focus ->
|
||||
(programs.range.start..<focus.row).sumOf {
|
||||
(programs.range.first..<focus.row).sumOf {
|
||||
val channelId = channels[it].id
|
||||
channelProgramCount[channelId] ?: 0
|
||||
} + focus.column
|
||||
|
|
@ -225,7 +221,7 @@ fun TvGuideGrid(
|
|||
timelineHourWidth = 240.dp,
|
||||
timelineHeight = 32.dp,
|
||||
channelWidth = 120.dp,
|
||||
channelHeight = 80.dp,
|
||||
channelHeight = 64.dp,
|
||||
currentTimeWidth = 2.dp,
|
||||
)
|
||||
var gridHasFocus by rememberSaveable { mutableStateOf(false) }
|
||||
|
|
@ -251,6 +247,8 @@ fun TvGuideGrid(
|
|||
if (item.column > 0) {
|
||||
// Not at beginning of row, so move to beginning
|
||||
item.copy(column = 0)
|
||||
} else if (item.row > 0) {
|
||||
item.copy(row = 0)
|
||||
} else {
|
||||
// At beginning, so allow normal back button behavior
|
||||
return@onPreviewKeyEvent false
|
||||
|
|
@ -496,56 +494,7 @@ fun TvGuideGrid(
|
|||
val program = programs.programs[programIndex]
|
||||
val focused =
|
||||
gridHasFocus && !channelColumnFocused && programIndex == focusedProgramIndex
|
||||
val background =
|
||||
if (focused) {
|
||||
MaterialTheme.colorScheme.inverseSurface
|
||||
} else {
|
||||
program.category?.color
|
||||
?: MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)
|
||||
}
|
||||
val textColor = MaterialTheme.colorScheme.contentColorFor(background)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(2.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
background,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = program.name ?: program.id.toString(),
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
modifier = Modifier.enableMarquee(focused),
|
||||
)
|
||||
listOfNotNull(
|
||||
program.seasonEpisode?.let { "S${it.season} E${it.episode}" },
|
||||
program.subtitle,
|
||||
).joinToString(" - ")
|
||||
.ifBlank { null }
|
||||
?.let {
|
||||
Text(
|
||||
text = it,
|
||||
color = textColor,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
RecordingMarker(
|
||||
isRecording = program.isRecording,
|
||||
isSeriesRecording = program.isSeriesRecording,
|
||||
modifier = Modifier.align(Alignment.BottomEnd),
|
||||
)
|
||||
}
|
||||
Program(program, focused, Modifier)
|
||||
}
|
||||
|
||||
channels(
|
||||
|
|
@ -557,42 +506,12 @@ fun TvGuideGrid(
|
|||
val channel = channels[channelIndex]
|
||||
val focused =
|
||||
gridHasFocus && channelColumnFocused && focusedChannelIndex == channelIndex
|
||||
val background =
|
||||
if (focused) {
|
||||
MaterialTheme.colorScheme.inverseSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.surface
|
||||
}
|
||||
val textColor = MaterialTheme.colorScheme.contentColorFor(background)
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
background,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Text(
|
||||
text = channel.number ?: channel.name ?: channelIndex.toString(),
|
||||
color = textColor,
|
||||
modifier = Modifier,
|
||||
)
|
||||
AsyncImage(
|
||||
model = channel.imageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxHeight(.66f),
|
||||
)
|
||||
}
|
||||
}
|
||||
Channel(
|
||||
channel = channel,
|
||||
channelIndex = channelIndex,
|
||||
focused = focused,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
topCorner {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue