diff --git a/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/AppCard.kt b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/AppCard.kt index c8de8b1..b3f20b0 100644 --- a/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/AppCard.kt +++ b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/AppCard.kt @@ -1,11 +1,13 @@ package nl.ndat.tvlauncher.ui.component.card import android.content.Intent -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -22,7 +24,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.scale import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource @@ -32,6 +33,7 @@ import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import nl.ndat.tvlauncher.R import nl.ndat.tvlauncher.data.entity.App +import nl.ndat.tvlauncher.ui.indication.FocusScaleIndication import nl.ndat.tvlauncher.util.createDrawable import nl.ndat.tvlauncher.util.ifElse @@ -44,14 +46,15 @@ fun AppCard( val context = LocalContext.current val image = remember { app.createDrawable(context) } var focused by remember { mutableStateOf(false) } - val scale = animateFloatAsState(if (focused) 1.125f else 1.0f) + val interactionSource = remember { MutableInteractionSource() } val launchIntentUri = app.launchIntentUriLeanback ?: app.launchIntentUriDefault Column( modifier = modifier .width(160.dp) - .scale(scale.value) + .focusable(true, interactionSource) + .indication(interactionSource, FocusScaleIndication(1.125f)) .onFocusChanged { focused = it.hasFocus } .clickable(enabled = launchIntentUri != null) { if (launchIntentUri != null) context.startActivity( diff --git a/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/ChannelProgramCard.kt b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/ChannelProgramCard.kt index a3b1589..c2b937c 100644 --- a/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/ChannelProgramCard.kt +++ b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/component/card/ChannelProgramCard.kt @@ -1,11 +1,13 @@ package nl.ndat.tvlauncher.ui.component.card import android.content.Intent -import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.aspectRatio @@ -23,7 +25,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.scale import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource @@ -33,6 +34,7 @@ import androidx.compose.ui.unit.sp import coil.compose.AsyncImage import nl.ndat.tvlauncher.R import nl.ndat.tvlauncher.data.entity.ChannelProgram +import nl.ndat.tvlauncher.ui.indication.FocusScaleIndication import nl.ndat.tvlauncher.util.ifElse @OptIn(ExperimentalFoundationApi::class) @@ -43,12 +45,13 @@ fun ChannelProgramCard( ) { val context = LocalContext.current var focused by remember { mutableStateOf(false) } - val scale = animateFloatAsState(if (focused) 1.125f else 1.0f) + val interactionSource = remember { MutableInteractionSource() } Column( modifier = modifier .width(90.dp * (program.posterArtAspectRatio?.floatValue ?: 1f)) - .scale(scale.value) + .focusable(true, interactionSource) + .indication(interactionSource, FocusScaleIndication(1.125f)) .onFocusChanged { focused = it.hasFocus } .clickable(enabled = program.intentUri != null) { if (program.intentUri != null) context.startActivity( diff --git a/app/src/main/kotlin/nl/ndat/tvlauncher/ui/indication/scale.kt b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/indication/scale.kt new file mode 100644 index 0000000..261ed8f --- /dev/null +++ b/app/src/main/kotlin/nl/ndat/tvlauncher/ui/indication/scale.kt @@ -0,0 +1,39 @@ +package nl.ndat.tvlauncher.ui.indication + +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.Indication +import androidx.compose.foundation.IndicationInstance +import androidx.compose.foundation.interaction.FocusInteraction +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.graphics.drawscope.scale + +private class ScaleIndicationInstance( + private val scale: Float, +) : IndicationInstance { + override fun ContentDrawScope.drawIndication() { + scale(scale) { + this@drawIndication.drawContent() + } + } +} + +class FocusScaleIndication( + private val focusedScale: Float, +) : Indication { + @Composable + override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance { + val interaction by interactionSource.interactions.collectAsState(initial = null) + val currentScale = when (interaction) { + is FocusInteraction.Focus -> focusedScale + else -> 1.0f + } + + val scale by animateFloatAsState(currentScale) + return remember(scale) { ScaleIndicationInstance(scale) } + } +} \ No newline at end of file