Fix TV detail playback single-press behavior

This commit is contained in:
2026-04-06 20:38:05 +02:00
parent 9e8772467c
commit b91a8a81e8
7 changed files with 99 additions and 2 deletions

View File

@@ -7,11 +7,16 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import androidx.compose.ui.input.key.Key
import hu.bbara.purefin.core.model.CastMember
import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.ui.theme.AppTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import java.util.UUID
@@ -66,6 +71,32 @@ class EpisodeScreenContentTest {
composeRule.onAllNodesWithText("Series").assertCountEquals(0)
}
@Test
fun episodeScreenContent_startsPlaybackOnFirstCenterPress() {
var playCount = 0
composeRule.setContent {
AppTheme {
EpisodeScreenContent(
episode = sampleEpisode(progress = 63.0),
seriesTitle = "Severance",
onPlay = { playCount++ }
)
}
}
composeRule.waitForIdle()
composeRule.onNodeWithTag(EpisodePlayButtonTag).assertIsFocused()
composeRule.onRoot().performKeyInput {
pressKey(Key.DirectionCenter)
}
composeRule.waitForIdle()
assertEquals(1, playCount)
}
private fun sampleEpisode(progress: Double?): Episode {
val seriesId = UUID.fromString("11111111-1111-1111-1111-111111111111")
val seasonId = UUID.fromString("22222222-2222-2222-2222-222222222222")

View File

@@ -7,11 +7,16 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import androidx.compose.ui.input.key.Key
import hu.bbara.purefin.core.model.CastMember
import hu.bbara.purefin.core.model.Movie
import hu.bbara.purefin.ui.theme.AppTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import java.util.UUID
@@ -43,6 +48,31 @@ class MovieScreenContentTest {
.assertIsFocused()
}
@Test
fun movieScreenContent_startsPlaybackOnFirstCenterPress() {
var playCount = 0
composeRule.setContent {
AppTheme {
MovieScreenContent(
movie = sampleMovie(progress = 42.0),
onPlay = { playCount++ }
)
}
}
composeRule.waitForIdle()
composeRule.onNodeWithTag(MoviePlayButtonTag).assertIsFocused()
composeRule.onRoot().performKeyInput {
pressKey(Key.DirectionCenter)
}
composeRule.waitForIdle()
assertEquals(1, playCount)
}
private fun sampleMovie(progress: Double?): Movie {
return Movie(
id = UUID.fromString("11111111-1111-1111-1111-111111111111"),

View File

@@ -5,13 +5,18 @@ import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performKeyInput
import androidx.compose.ui.test.pressKey
import androidx.compose.ui.input.key.Key
import hu.bbara.purefin.core.model.CastMember
import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.core.model.Season
import hu.bbara.purefin.core.model.Series
import hu.bbara.purefin.ui.theme.AppTheme
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import java.util.UUID
@@ -65,6 +70,31 @@ class SeriesScreenContentTest {
.assertIsFocused()
}
@Test
fun seriesScreenContent_startsPlaybackOnFirstCenterPress() {
var playCount = 0
composeRule.setContent {
AppTheme {
SeriesScreenContent(
series = sampleSeriesWithEpisodes(),
onPlayEpisode = { playCount++ }
)
}
}
composeRule.waitForIdle()
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsFocused()
composeRule.onRoot().performKeyInput {
pressKey(Key.DirectionCenter)
}
composeRule.waitForIdle()
assertEquals(1, playCount)
}
private fun sampleSeriesWithEpisodes(): Series {
val seriesId = UUID.fromString("11111111-1111-1111-1111-111111111111")
val seasonId = UUID.fromString("22222222-2222-2222-2222-222222222222")

View File

@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp
@@ -75,6 +76,7 @@ internal fun EpisodeScreenContent(
val playFocusRequester = remember { FocusRequester() }
LaunchedEffect(episode.id) {
withFrameNanos { }
playFocusRequester.requestFocus()
}

View File

@@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp
@@ -54,6 +55,7 @@ internal fun MovieScreenContent(
val playFocusRequester = remember { FocusRequester() }
LaunchedEffect(movie.id) {
withFrameNanos { }
playFocusRequester.requestFocus()
}

View File

@@ -7,10 +7,11 @@ import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.withFrameNanos
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.unit.dp
@@ -63,6 +64,7 @@ internal fun SeriesScreenContent(
val firstContentFocusRequester = remember { FocusRequester() }
LaunchedEffect(series.id, nextUpEpisode?.id) {
withFrameNanos { }
if (nextUpEpisode != null) {
playFocusRequester.requestFocus()
} else {

View File

@@ -68,8 +68,8 @@ fun MediaResumeButton(
.border(3.dp, focusBorderColor, focusShape)
.clip(focusShape)
.onFocusChanged { isFocused = it.isFocused || it.hasFocus }
.focusable()
.clickable(onClick = onClick)
.focusable()
) {
// Bottom layer: inverted colors (visible for the remaining %)
Box(