From 1335f10e4f9dc90024774f9aedabbe8550bca190 Mon Sep 17 00:00:00 2001 From: Jean-Simon Bonin-L'Herault Date: Sun, 19 Nov 2023 20:43:53 -0500 Subject: [PATCH] Compose android tv bindings (#202) --- buildSrc/src/main/kotlin/Project.kt | 1 + buildSrc/src/main/kotlin/Versions.kt | 1 + settings.gradle.kts | 1 + .../compose-tv-flow/api/compose-tv-flow.api | 36 +++ .../compose-tv-flow/build.gradle.kts | 59 ++++ .../compose/viewmodel/VMDButtonTv.kt | 61 +++++ .../compose/viewmodel/VMDCheckboxTv.kt | 99 +++++++ .../compose/viewmodel/VMDSwitchTv.kt | 104 +++++++ .../compose/viewmodel/VMDTextTv.kt | 68 +++++ .../sample/android/build.gradle.kts | 6 +- .../android/src/main/AndroidManifest.xml | 16 ++ .../mirego/sample/ui/home/HomeTvActivity.kt | 24 ++ .../com/mirego/sample/ui/tv/HomeTvView.kt | 99 +++++++ .../ui/tv/showcase/ButtonTvShowcaseView.kt | 149 ++++++++++ .../ui/tv/showcase/CarouselTvShowcaseView.kt | 76 ++++++ .../ui/tv/showcase/TabRowTvShowcaseView.kt | 100 +++++++ .../ui/tv/showcase/TextTvShowcaseView.kt | 60 +++++ .../ui/tv/showcase/ToggleTvShowcaseView.kt | 255 ++++++++++++++++++ .../SampleViewModelControllerFactory.kt | 3 + .../SampleViewModelControllerFactoryImpl.kt | 3 + .../carousel/CarouselShowcaseViewModel.kt | 14 + .../carousel/CarouselShowcaseViewModelImpl.kt | 30 +++ .../viewmodels/tv/HomeMenuSectionItem.kt | 33 +++ .../viewmodels/tv/HomeTvNavigationDelegate.kt | 5 + .../sample/viewmodels/tv/HomeTvViewModel.kt | 7 + .../tv/HomeTvViewModelController.kt | 8 + .../viewmodels/tv/HomeTvViewModelImpl.kt | 39 +++ .../sample/viewmodels/tv/MenuSectionItem.kt | 9 + .../translations/translation.en.json | 2 + 29 files changed, 1367 insertions(+), 1 deletion(-) create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/api/compose-tv-flow.api create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/build.gradle.kts create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDButtonTv.kt create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDCheckboxTv.kt create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDSwitchTv.kt create mode 100644 trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDTextTv.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/home/HomeTvActivity.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/HomeTvView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ButtonTvShowcaseView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/CarouselTvShowcaseView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TabRowTvShowcaseView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TextTvShowcaseView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ToggleTvShowcaseView.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModel.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModelImpl.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeMenuSectionItem.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvNavigationDelegate.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModel.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelController.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelImpl.kt create mode 100644 trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/MenuSectionItem.kt diff --git a/buildSrc/src/main/kotlin/Project.kt b/buildSrc/src/main/kotlin/Project.kt index bddac3a8..703d36ea 100644 --- a/buildSrc/src/main/kotlin/Project.kt +++ b/buildSrc/src/main/kotlin/Project.kt @@ -16,6 +16,7 @@ object Project { const val TRIKOT_VIEWMODELS_DECLARATIVE_SAMPLE_COMMON = ":trikot-viewmodels-declarative:sample:common" const val TRIKOT_VIEWMODELS_DECLARATIVE_FLOW = ":trikot-viewmodels-declarative-flow:viewmodels-declarative-flow" const val TRIKOT_VIEWMODELS_DECLARATIVE_COMPOSE_FLOW = ":trikot-viewmodels-declarative-flow:compose-flow" + const val TRIKOT_VIEWMODELS_DECLARATIVE_COMPOSE_TV_FLOW = ":trikot-viewmodels-declarative-flow:compose-tv-flow" const val TRIKOT_VIEWMODELS_DECLARATIVE_SAMPLE_COMMON_FLOW = ":trikot-viewmodels-declarative-flow:sample:common" const val TRIKOT_VIEWMODELS_DECLARATIVE_ANNOTATIONS = ":trikot-viewmodels-declarative-annotations:viewmodels-annotations" const val TRIKOT_VIEWMODELS_DECLARATIVE_COMPILER_CORE = ":trikot-viewmodels-declarative-compiler:viewmodels-declarative-compiler-core" diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 5d619ead..f48e6a61 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -5,6 +5,7 @@ object Versions { const val JETPACK_COMPOSE_COMPILER = "1.5.3" const val JETPACK_COMPOSE_RUNTIME = "1.5.0" const val JETPACK_COMPOSE_MATERIAL_3 = "1.1.1" + const val JETPACK_COMPOSE_TV = "1.0.0-alpha10" const val COIL = "2.0.0-rc03" const val KTLINT = "11.6.1" const val KOTLINX_SERIALIZATION = "1.6.0" diff --git a/settings.gradle.kts b/settings.gradle.kts index 8974c313..522649e5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,6 +44,7 @@ include(":trikot-viewmodels-declarative:viewmodels-declarative") include(":trikot-viewmodels-declarative:compose") include(":trikot-viewmodels-declarative-flow:viewmodels-declarative-flow") include(":trikot-viewmodels-declarative-flow:compose-flow") +include(":trikot-viewmodels-declarative-flow:compose-tv-flow") include(":trikot-viewmodels-declarative-annotations:viewmodels-annotations") include(":trikot-viewmodels-declarative-compiler:viewmodels-declarative-compiler-core") include(":trikot-viewmodels-declarative-compiler:viewmodels-declarative-compiler-streams") diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/api/compose-tv-flow.api b/trikot-viewmodels-declarative-flow/compose-tv-flow/api/compose-tv-flow.api new file mode 100644 index 00000000..68cba7ce --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/api/compose-tv-flow.api @@ -0,0 +1,36 @@ +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/ComposableSingletons$VMDCheckboxTvKt { + public static final field INSTANCE Lcom/mirego/trikot/viewmodels/declarative/compose/viewmodel/ComposableSingletons$VMDCheckboxTvKt; + public static field lambda-1 Lkotlin/jvm/functions/Function4; + public static field lambda-2 Lkotlin/jvm/functions/Function4; + public fun ()V + public final fun getLambda-1$compose_tv_flow_release ()Lkotlin/jvm/functions/Function4; + public final fun getLambda-2$compose_tv_flow_release ()Lkotlin/jvm/functions/Function4; +} + +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/ComposableSingletons$VMDSwitchTvKt { + public static final field INSTANCE Lcom/mirego/trikot/viewmodels/declarative/compose/viewmodel/ComposableSingletons$VMDSwitchTvKt; + public static field lambda-1 Lkotlin/jvm/functions/Function4; + public static field lambda-2 Lkotlin/jvm/functions/Function4; + public fun ()V + public final fun getLambda-1$compose_tv_flow_release ()Lkotlin/jvm/functions/Function4; + public final fun getLambda-2$compose_tv_flow_release ()Lkotlin/jvm/functions/Function4; +} + +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDButtonTvKt { + public static final fun VMDButtonTv-121YqSk (Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDButtonViewModel;Landroidx/tv/material3/ButtonGlow;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/compose/foundation/layout/PaddingValues;Landroidx/tv/material3/ButtonBorder;FLandroidx/tv/material3/ButtonColors;Landroidx/tv/material3/ButtonShape;Landroidx/tv/material3/ButtonScale;Lkotlin/jvm/functions/Function5;Landroidx/compose/runtime/Composer;III)V +} + +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDCheckboxTvKt { + public static final fun VMDCheckboxTv (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDToggleViewModel;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/tv/material3/CheckboxColors;Landroidx/compose/runtime/Composer;II)V + public static final fun VMDCheckboxTv (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDToggleViewModel;Lkotlin/jvm/functions/Function4;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/tv/material3/CheckboxColors;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDSwitchTvKt { + public static final fun VMDSwitchTv (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDToggleViewModel;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/tv/material3/SwitchColors;Landroidx/compose/runtime/Composer;II)V + public static final fun VMDSwitchTv (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDToggleViewModel;Lkotlin/jvm/functions/Function4;Landroidx/compose/foundation/interaction/MutableInteractionSource;Landroidx/tv/material3/SwitchColors;Landroidx/compose/runtime/Composer;II)V +} + +public final class com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDTextTvKt { + public static final fun VMDTextTv--4IGK_g (Landroidx/compose/ui/Modifier;Lcom/mirego/trikot/viewmodels/declarative/components/VMDTextViewModel;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILjava/util/Map;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V +} + diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/build.gradle.kts b/trikot-viewmodels-declarative-flow/compose-tv-flow/build.gradle.kts new file mode 100644 index 00000000..344ae70e --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + id("com.android.library") + id("kotlin-android") + id("mirego.publish") +} + +group = "com.mirego.trikot" + +android { + namespace = "com.mirego.trikot.vmd.flow.compose.tv" + defaultConfig { + compileSdk = Versions.Android.COMPILE_SDK + minSdk = Versions.Android.MIN_SDK + } + buildFeatures { + compose = true + buildConfig = false + } + compileOptions { + sourceCompatibility(JavaVersion.VERSION_17) + targetCompatibility(JavaVersion.VERSION_17) + } + composeOptions { + kotlinCompilerExtensionVersion = Versions.JETPACK_COMPOSE_COMPILER + } + sourceSets.configureEach { + java.srcDirs("src/$name/kotlin") + } +} + +dependencies { + implementation(project(Project.TRIKOT_VIEWMODELS_DECLARATIVE_FLOW)) + implementation(project(Project.TRIKOT_FOUNDATION)) + implementation(project(Project.TRIKOT_VIEWMODELS_DECLARATIVE_COMPOSE_FLOW)) + api("androidx.compose.foundation:foundation:${Versions.JETPACK_COMPOSE_RUNTIME}") + api("androidx.compose.material:material:${Versions.JETPACK_COMPOSE_RUNTIME}") + api("androidx.compose.runtime:runtime:${Versions.JETPACK_COMPOSE_RUNTIME}") + api("androidx.compose.ui:ui-tooling:${Versions.JETPACK_COMPOSE_RUNTIME}") + api("androidx.compose.material3:material3:${Versions.JETPACK_COMPOSE_MATERIAL_3}") + api("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1") + api("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1") + api("io.coil-kt:coil-compose:${Versions.COIL}") + implementation("com.google.accompanist:accompanist-drawablepainter:${Versions.ACCOMPANIST}") + implementation("androidx.appcompat:appcompat:1.5.1") + implementation("org.jetbrains.kotlin:kotlin-reflect:${Versions.KOTLIN}") + implementation("androidx.tv:tv-foundation:${Versions.JETPACK_COMPOSE_TV}") + implementation("androidx.tv:tv-material:${Versions.JETPACK_COMPOSE_TV}") +} + +afterEvaluate { + publishing { + publications { + create("release") { + from(components["release"]) + artifactId = "viewmodels-declarative-compose-tv-flow" + } + } + } +} diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDButtonTv.kt b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDButtonTv.kt new file mode 100644 index 00000000..8f286ec5 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDButtonTv.kt @@ -0,0 +1,61 @@ +package com.mirego.trikot.viewmodels.declarative.compose.viewmodel + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.tv.material3.Button +import androidx.tv.material3.ButtonBorder +import androidx.tv.material3.ButtonColors +import androidx.tv.material3.ButtonDefaults +import androidx.tv.material3.ButtonGlow +import androidx.tv.material3.ButtonScale +import androidx.tv.material3.ButtonShape +import androidx.tv.material3.ExperimentalTvMaterial3Api +import com.mirego.trikot.viewmodels.declarative.components.VMDButtonViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.isOverridingAlpha +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.extensions.vmdModifier +import com.mirego.trikot.viewmodels.declarative.content.VMDContent + +@ExperimentalTvMaterial3Api +@Composable +fun VMDButtonTv( + modifier: Modifier = Modifier, + viewModel: VMDButtonViewModel, + glow: ButtonGlow = ButtonDefaults.glow(), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + contentPadding: PaddingValues = ButtonDefaults.ContentPadding, + border: ButtonBorder = ButtonDefaults.border(), + tonalElevation: Dp = 0.0.dp, + colors: ButtonColors = ButtonDefaults.colors(), + shape: ButtonShape = ButtonDefaults.shape(), + scale: ButtonScale = ButtonDefaults.scale(), + content: @Composable (RowScope.(field: C, isFocused: Boolean) -> Unit) +) { + val buttonViewModel: VMDButtonViewModel by viewModel.observeAsState(excludedProperties = if (modifier.isOverridingAlpha()) listOf(viewModel::isHidden) else emptyList()) + + val isFocused = interactionSource.collectIsFocusedAsState().value + + Button( + modifier = modifier + .vmdModifier(buttonViewModel), + onClick = buttonViewModel.actionBlock, + enabled = buttonViewModel.isEnabled, + glow = glow, + interactionSource = interactionSource, + contentPadding = contentPadding, + border = border, + tonalElevation = tonalElevation, + colors = colors, + shape = shape, + scale = scale, + content = { content(buttonViewModel.content, isFocused) } + ) +} diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDCheckboxTv.kt b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDCheckboxTv.kt new file mode 100644 index 00000000..03e6c5fa --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDCheckboxTv.kt @@ -0,0 +1,99 @@ +@file:OptIn(ExperimentalTvMaterial3Api::class) + +package com.mirego.trikot.viewmodels.declarative.compose.viewmodel + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.selection.toggleable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview +import androidx.tv.material3.Checkbox +import androidx.tv.material3.CheckboxColors +import androidx.tv.material3.CheckboxDefaults +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.Text +import com.mirego.trikot.viewmodels.declarative.components.VMDToggleViewModel +import com.mirego.trikot.viewmodels.declarative.components.factory.VMDComponents +import com.mirego.trikot.viewmodels.declarative.compose.extensions.isOverridingAlpha +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.extensions.vmdModifier +import com.mirego.trikot.viewmodels.declarative.content.VMDContent +import com.mirego.trikot.viewmodels.declarative.content.VMDNoContent +import kotlinx.coroutines.MainScope + +@ExperimentalTvMaterial3Api +@Composable +fun VMDCheckboxTv( + modifier: Modifier = Modifier, + componentModifier: Modifier = Modifier, + viewModel: VMDToggleViewModel, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + colors: CheckboxColors = CheckboxDefaults.colors() +) { + VMDCheckboxTv( + modifier = modifier, + componentModifier = componentModifier, + viewModel = viewModel, + label = {}, + interactionSource = interactionSource, + colors = colors + ) +} + +@Composable +fun VMDCheckboxTv( + modifier: Modifier = Modifier, + componentModifier: Modifier = Modifier, + viewModel: VMDToggleViewModel, + label: @Composable (RowScope.(field: C) -> Unit), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + colors: CheckboxColors = CheckboxDefaults.colors() +) { + val toggleViewModel: VMDToggleViewModel by viewModel.observeAsState(excludedProperties = if (modifier.isOverridingAlpha()) listOf(viewModel::isHidden) else emptyList()) + + VMDLabeledComponent( + modifier = modifier + .toggleable( + value = toggleViewModel.isOn, + role = Role.Checkbox, + onValueChange = { checked -> viewModel.onValueChange(checked) }, + ) + .vmdModifier(toggleViewModel), + label = { label(toggleViewModel.label) }, + content = { + Checkbox( + onCheckedChange = null, + modifier = componentModifier, + enabled = toggleViewModel.isEnabled, + checked = toggleViewModel.isOn, + interactionSource = interactionSource, + colors = colors, + ) + } + ) +} + +@Preview +@Composable +private fun EnabledToggleCheckboxPreview() { + val toggleViewModel = VMDComponents.Toggle.withState(true, MainScope()) + VMDCheckboxTv(viewModel = toggleViewModel) +} + +@Preview +@Composable +private fun DisabledToggleCheckboxPreview() { + val toggleViewModel = VMDComponents.Toggle.withState(false, MainScope()) + VMDCheckboxTv(viewModel = toggleViewModel) +} + +@Preview +@Composable +private fun SimpleTextToggleCheckboxPreview() { + val toggleViewModel = VMDComponents.Toggle.withText("Label", true, MainScope()) + VMDCheckboxTv(viewModel = toggleViewModel, label = { Text(it.text) }) +} diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDSwitchTv.kt b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDSwitchTv.kt new file mode 100644 index 00000000..ffd9c190 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDSwitchTv.kt @@ -0,0 +1,104 @@ +package com.mirego.trikot.viewmodels.declarative.compose.viewmodel + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.selection.toggleable +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.tooling.preview.Preview +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.Switch +import androidx.tv.material3.SwitchColors +import androidx.tv.material3.SwitchDefaults +import androidx.tv.material3.Text +import com.mirego.trikot.viewmodels.declarative.components.VMDToggleViewModel +import com.mirego.trikot.viewmodels.declarative.components.factory.VMDComponents +import com.mirego.trikot.viewmodels.declarative.compose.extensions.isOverridingAlpha +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.extensions.vmdModifier +import com.mirego.trikot.viewmodels.declarative.content.VMDContent +import com.mirego.trikot.viewmodels.declarative.content.VMDNoContent +import kotlinx.coroutines.MainScope + +@ExperimentalTvMaterial3Api +@Composable +fun VMDSwitchTv( + modifier: Modifier = Modifier, + componentModifier: Modifier = Modifier, + viewModel: VMDToggleViewModel, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + colors: SwitchColors = SwitchDefaults.colors() +) { + VMDSwitchTv( + modifier = modifier, + componentModifier = componentModifier, + viewModel = viewModel, + label = {}, + interactionSource = interactionSource, + colors = colors + ) +} + +@ExperimentalTvMaterial3Api +@Composable +fun VMDSwitchTv( + modifier: Modifier = Modifier, + componentModifier: Modifier = Modifier, + viewModel: VMDToggleViewModel, + label: @Composable (RowScope.(field: C) -> Unit), + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + colors: SwitchColors = SwitchDefaults.colors() +) { + val toggleViewModel: VMDToggleViewModel by viewModel.observeAsState(excludedProperties = if (modifier.isOverridingAlpha()) listOf(viewModel::isHidden) else emptyList()) + + VMDLabeledComponent( + modifier = modifier + .toggleable( + value = toggleViewModel.isOn, + role = Role.Switch, + onValueChange = { checked -> viewModel.onValueChange(checked) }, + ) + .vmdModifier(toggleViewModel), + label = { label(toggleViewModel.label) }, + content = { + Switch( + modifier = componentModifier, + enabled = toggleViewModel.isEnabled, + checked = toggleViewModel.isOn, + colors = colors, + interactionSource = interactionSource, + onCheckedChange = null + ) + } + ) +} + +@ExperimentalTvMaterial3Api +@Preview +@Composable +private fun EnabledSwitchPreview() { + val toggleViewModel = + VMDComponents.Toggle.withState(true, MainScope()) + VMDSwitchTv(viewModel = toggleViewModel) +} + +@ExperimentalTvMaterial3Api +@Preview +@Composable +private fun DisabledSwitchPreview() { + val toggleViewModel = + VMDComponents.Toggle.withState(false, MainScope()) + VMDSwitchTv(viewModel = toggleViewModel) +} + +@ExperimentalTvMaterial3Api +@Preview +@Composable +private fun SimpleTextSwitchPreview() { + val toggleViewModel = + VMDComponents.Toggle.withText("Label", true, MainScope()) + VMDSwitchTv(viewModel = toggleViewModel, label = { Text(it.text) }) +} diff --git a/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDTextTv.kt b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDTextTv.kt new file mode 100644 index 00000000..75fa62e0 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/compose-tv-flow/src/main/kotlin/com/mirego/trikot/viewmodels/declarative/compose/viewmodel/VMDTextTv.kt @@ -0,0 +1,68 @@ +package com.mirego.trikot.viewmodels.declarative.compose.viewmodel + +import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextLayoutResult +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.TextUnit +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.LocalTextStyle +import androidx.tv.material3.Text +import com.mirego.trikot.viewmodels.declarative.components.VMDTextViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.isOverridingAlpha +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.extensions.toAnnotatedString +import com.mirego.trikot.viewmodels.declarative.compose.extensions.vmdModifier + +@ExperimentalTvMaterial3Api +@Composable +fun VMDTextTv( + modifier: Modifier = Modifier, + viewModel: VMDTextViewModel, + color: Color = Color.Unspecified, + fontSize: TextUnit = TextUnit.Unspecified, + fontStyle: FontStyle? = null, + fontWeight: FontWeight? = null, + fontFamily: FontFamily? = null, + letterSpacing: TextUnit = TextUnit.Unspecified, + textDecoration: TextDecoration? = null, + textAlign: TextAlign? = null, + lineHeight: TextUnit = TextUnit.Unspecified, + overflow: TextOverflow = TextOverflow.Clip, + softWrap: Boolean = true, + maxLines: Int = Int.MAX_VALUE, + inlineContent: Map = mapOf(), + onTextLayout: (TextLayoutResult) -> Unit = {}, + style: TextStyle = LocalTextStyle.current +) { + val textViewModel by viewModel.observeAsState(excludedProperties = if (modifier.isOverridingAlpha()) listOf(viewModel::isHidden) else emptyList()) + + Text( + text = textViewModel.toAnnotatedString(), + modifier = modifier.vmdModifier(textViewModel), + color = color, + fontSize = fontSize, + fontStyle = fontStyle, + fontWeight = fontWeight, + fontFamily = fontFamily, + letterSpacing = letterSpacing, + textDecoration = textDecoration, + textAlign = textAlign, + lineHeight = lineHeight, + overflow = overflow, + softWrap = softWrap, + maxLines = maxLines, + onTextLayout = onTextLayout, + inlineContent = inlineContent, + style = style + ) +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/build.gradle.kts b/trikot-viewmodels-declarative-flow/sample/android/build.gradle.kts index a483c2ba..fadc4477 100644 --- a/trikot-viewmodels-declarative-flow/sample/android/build.gradle.kts +++ b/trikot-viewmodels-declarative-flow/sample/android/build.gradle.kts @@ -47,7 +47,8 @@ android { kotlinOptions { freeCompilerArgs = freeCompilerArgs + listOf( - "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api" + "-opt-in=androidx.compose.material3.ExperimentalMaterial3Api", + "-opt-in=androidx.compose.material3.ExperimentalTvMaterial3Api" ) } @@ -67,10 +68,13 @@ android { dependencies { implementation(project(Project.TRIKOT_VIEWMODELS_DECLARATIVE_SAMPLE_COMMON_FLOW)) implementation(project(Project.TRIKOT_VIEWMODELS_DECLARATIVE_COMPOSE_FLOW)) + implementation(project(Project.TRIKOT_VIEWMODELS_DECLARATIVE_COMPOSE_TV_FLOW)) implementation("androidx.appcompat:appcompat:1.4.0") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:${Versions.ANDROIDX_LIFECYCLE}") implementation("androidx.lifecycle:lifecycle-runtime-ktx:${Versions.ANDROIDX_LIFECYCLE}") implementation("androidx.lifecycle:lifecycle-common-java8:${Versions.ANDROIDX_LIFECYCLE}") + implementation("androidx.tv:tv-foundation:${Versions.JETPACK_COMPOSE_TV}") + implementation("androidx.tv:tv-material:${Versions.JETPACK_COMPOSE_TV}") implementation("androidx.activity:activity-compose:1.4.0") implementation("com.google.android.material:material:1.4.0") implementation("com.squareup.picasso:picasso:2.71828") diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/AndroidManifest.xml b/trikot-viewmodels-declarative-flow/sample/android/src/main/AndroidManifest.xml index 1814b833..09c65503 100644 --- a/trikot-viewmodels-declarative-flow/sample/android/src/main/AndroidManifest.xml +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/AndroidManifest.xml @@ -4,11 +4,14 @@ + + + + + + + + + + + diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/home/HomeTvActivity.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/home/HomeTvActivity.kt new file mode 100644 index 00000000..515b0f98 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/home/HomeTvActivity.kt @@ -0,0 +1,24 @@ +package com.mirego.sample.ui.home + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.tv.material3.ExperimentalTvMaterial3Api +import com.mirego.sample.ui.tv.HomeTvView +import com.mirego.sample.viewmodels.tv.HomeTvNavigationDelegate +import com.mirego.sample.viewmodels.tv.HomeTvViewModel +import com.mirego.sample.viewmodels.tv.HomeTvViewModelController +import com.mirego.trikot.viewmodels.declarative.controller.ViewModelActivity + +@OptIn(ExperimentalTvMaterial3Api::class) +class HomeTvActivity : ViewModelActivity(), HomeTvNavigationDelegate { + override val viewModelController: HomeTvViewModelController by lazy { + getViewModelController(HomeTvViewModelController::class) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + HomeTvView(viewModel) + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/HomeTvView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/HomeTvView.kt new file mode 100644 index 00000000..160c5913 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/HomeTvView.kt @@ -0,0 +1,99 @@ +@file:OptIn(ExperimentalTvMaterial3Api::class) + +package com.mirego.sample.ui.tv + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.NavigationDrawerItem +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.tv.material3.DrawerValue +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.NavigationDrawer +import androidx.tv.material3.Text +import com.mirego.sample.ui.tv.showcase.ButtonTvShowcaseView +import com.mirego.sample.ui.tv.showcase.CarouselTvShowcaseView +import com.mirego.sample.ui.tv.showcase.TabRowTvShowcaseView +import com.mirego.sample.ui.tv.showcase.TextTvShowcaseView +import com.mirego.sample.ui.tv.showcase.ToggleTvShowcaseView +import com.mirego.sample.viewmodels.tv.HomeMenuSectionItem +import com.mirego.sample.viewmodels.tv.HomeTvViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState + +@ExperimentalTvMaterial3Api +@Composable +fun HomeTvView(homeTvViewModel: HomeTvViewModel) { + val viewModel: HomeTvViewModel by homeTvViewModel.observeAsState() + + val selectedIndex = remember { mutableIntStateOf(0) } + + NavigationDrawer( + drawerContent = { drawerValue -> + OpenDrawerMenuView( + viewModel = viewModel, + selectedIndex = selectedIndex, + drawerValue = drawerValue + ) + } + ) { + HomeContentView(viewModel, selectedIndex) + } +} + +@Composable +private fun HomeContentView(viewModel: HomeTvViewModel, selectedIndex: MutableState) { + when (val sectionViewModel = viewModel.menuItems[selectedIndex.value]) { + is HomeMenuSectionItem.TextShowcase -> TextTvShowcaseView(sectionViewModel.viewModel) + is HomeMenuSectionItem.ToggleShowcase -> ToggleTvShowcaseView(sectionViewModel.viewModel) + is HomeMenuSectionItem.ButtonShowcase -> ButtonTvShowcaseView(sectionViewModel.viewModel) + is HomeMenuSectionItem.CarouselShowcase -> CarouselTvShowcaseView(sectionViewModel.viewModel) + is HomeMenuSectionItem.TopNavigationShowcase -> TabRowTvShowcaseView(sectionViewModel.viewModel) + } +} + +@Composable +private fun OpenDrawerMenuView(viewModel: HomeTvViewModel, selectedIndex: MutableState, drawerValue: DrawerValue) { + val drawerWidth by animateDpAsState( + targetValue = when (drawerValue) { + DrawerValue.Closed -> 48.dp + DrawerValue.Open -> 200.dp + }, + label = "drawerWidth" + ) + Column( + Modifier + .background(Color.Gray) + .width(drawerWidth) + .fillMaxHeight() + .padding(12.dp) + .selectableGroup(), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + viewModel.menuItems.forEachIndexed { index, homeMenuSectionItem -> + NavigationDrawerItem( + selected = selectedIndex.value == index, + onClick = { selectedIndex.value = index }, + label = { + AnimatedVisibility(visible = drawerValue == DrawerValue.Open) { + Text(homeMenuSectionItem.title) + } + } + ) + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ButtonTvShowcaseView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ButtonTvShowcaseView.kt new file mode 100644 index 00000000..c960e333 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ButtonTvShowcaseView.kt @@ -0,0 +1,149 @@ +package com.mirego.sample.ui.tv.showcase + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.unit.dp +import androidx.tv.foundation.lazy.list.TvLazyColumn +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.MaterialTheme +import androidx.tv.material3.Text +import com.mirego.sample.ui.theming.SampleTextStyle +import com.mirego.sample.ui.theming.medium +import com.mirego.sample.viewmodels.showcase.components.button.ButtonShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.LocalImage +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDButtonTv +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDTextTv + +@ExperimentalTvMaterial3Api +@Composable +fun ButtonTvShowcaseView(buttonShowcaseViewModel: ButtonShowcaseViewModel) { + val viewModel: ButtonShowcaseViewModel by buttonShowcaseViewModel.observeAsState() + + TvLazyColumn( + contentPadding = PaddingValues(24.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + item { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + VMDButtonTv( + viewModel = viewModel.textButton + ) { content, _ -> + Text( + modifier = Modifier + .padding(start = 8.dp, end = 8.dp, top = 4.dp, bottom = 4.dp), + text = content.text, + style = SampleTextStyle.body.medium() + ) + } + VMDTextTv( + viewModel = viewModel.textButtonTitle, + color = Color.White + ) + } + } + item { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + VMDButtonTv( + viewModel = viewModel.imageButton + ) { content, isFocused -> + val contentColor = when (isFocused) { + true -> MaterialTheme.colorScheme.inverseOnSurface + false -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) + } + LocalImage( + modifier = Modifier + .padding(4.dp), + imageResource = content.image, + contentDescription = content.contentDescription, + colorFilter = ColorFilter.tint(contentColor) + ) + } + VMDTextTv( + viewModel = viewModel.imageButtonTitle, + color = Color.White + ) + } + } + item { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + VMDButtonTv( + viewModel = viewModel.textImageButton + ) { content, isFocused -> + val contentColor = when (isFocused) { + true -> MaterialTheme.colorScheme.inverseOnSurface + false -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) + } + Row( + modifier = Modifier + .padding(start = 8.dp, end = 8.dp, top = 4.dp, bottom = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + LocalImage( + modifier = Modifier.padding(end = 8.dp), + imageResource = content.image, + contentDescription = content.contentDescription, + colorFilter = ColorFilter.tint(contentColor) + ) + Text( + text = content.text, + style = SampleTextStyle.body.medium() + ) + } + } + VMDTextTv( + viewModel = viewModel.textImageButtonTitle, + color = Color.White + ) + } + } + item { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + VMDButtonTv( + viewModel = viewModel.textPairButton + ) { content, _ -> + + Column( + modifier = Modifier + .padding(start = 8.dp, end = 8.dp, top = 4.dp, bottom = 4.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = content.first, + style = SampleTextStyle.title2, + ) + Text( + text = content.second, + style = SampleTextStyle.body, + ) + } + } + VMDTextTv( + viewModel = viewModel.textPairButtonTitle, + color = Color.White + ) + } + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/CarouselTvShowcaseView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/CarouselTvShowcaseView.kt new file mode 100644 index 00000000..18635fe0 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/CarouselTvShowcaseView.kt @@ -0,0 +1,76 @@ +@file:OptIn(ExperimentalTvMaterial3Api::class) + +package com.mirego.sample.ui.tv.showcase + +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import androidx.tv.material3.Carousel +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.Text +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselItemContent +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDImage + +@ExperimentalTvMaterial3Api +@Composable +fun CarouselTvShowcaseView(carouselShowcaseViewModel: CarouselShowcaseViewModel) { + val viewModel: CarouselShowcaseViewModel by carouselShowcaseViewModel.observeAsState() + + Carousel( + modifier = Modifier + .padding(32.dp) + .fillMaxWidth(), + itemCount = viewModel.items.size, + ) { index -> + CarouselItemView(viewModel.items[index]) + } +} + +@Composable +private fun CarouselItemView(content: CarouselItemContent) { + Box(modifier = Modifier.clip(RoundedCornerShape(16.dp))) { + VMDImage( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + viewModel = content.image, + contentScale = ContentScale.Crop + ) + Column( + modifier = Modifier + .align(Alignment.BottomStart) + .padding(32.dp) + .clip(RoundedCornerShape(16.dp)) + .background(Color.Black.copy(alpha = 0.3f)) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = content.title, + color = Color.White, + style = MaterialTheme.typography.displayLarge + ) + Text( + text = content.description, + color = Color.White, + style = MaterialTheme.typography.bodyLarge + ) + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TabRowTvShowcaseView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TabRowTvShowcaseView.kt new file mode 100644 index 00000000..f9d96eb4 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TabRowTvShowcaseView.kt @@ -0,0 +1,100 @@ +@file:OptIn(ExperimentalTvMaterial3Api::class) + +package com.mirego.sample.ui.tv.showcase + +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.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.Tab +import androidx.tv.material3.TabRow +import androidx.tv.material3.Text +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselItemContent +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDImage + +@ExperimentalTvMaterial3Api +@Composable +fun TabRowTvShowcaseView(carouselShowcaseViewModel: CarouselShowcaseViewModel) { + val viewModel: CarouselShowcaseViewModel by carouselShowcaseViewModel.observeAsState() + Box(modifier = Modifier.fillMaxSize()) { + var selectedTabIndex by remember { mutableStateOf(0) } + TabRow( + modifier = Modifier + .align(Alignment.TopCenter) + .padding(top = 16.dp), + selectedTabIndex = selectedTabIndex + ) { + viewModel.items.forEachIndexed { index, item -> + Tab( + selected = selectedTabIndex == index, + onFocus = { + selectedTabIndex = index + }, + ) { + Text( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), + text = item.title, + style = MaterialTheme.typography.headlineMedium + ) + } + } + } + RowItemView(viewModel.items[selectedTabIndex]) + } +} + +@Composable +private fun RowItemView(content: CarouselItemContent) { + Box( + modifier = Modifier + .padding(horizontal = 32.dp) + .padding(top = 90.dp, bottom = 32.dp) + .clip(RoundedCornerShape(16.dp)) + ) { + VMDImage( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + viewModel = content.image, + contentScale = ContentScale.Crop + ) + Column( + modifier = Modifier + .align(Alignment.BottomStart) + .padding(32.dp) + .clip(RoundedCornerShape(16.dp)) + .background(Color.Black.copy(alpha = 0.3f)) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = content.title, + color = Color.White, + style = MaterialTheme.typography.displayLarge + ) + Text( + text = content.description, + color = Color.White, + style = MaterialTheme.typography.bodyLarge + ) + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TextTvShowcaseView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TextTvShowcaseView.kt new file mode 100644 index 00000000..5a789a9c --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/TextTvShowcaseView.kt @@ -0,0 +1,60 @@ +package com.mirego.sample.ui.tv.showcase + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.tv.foundation.lazy.list.TvLazyColumn +import androidx.tv.foundation.lazy.list.items +import androidx.tv.material3.Card +import androidx.tv.material3.ExperimentalTvMaterial3Api +import com.mirego.sample.ui.theming.SampleTextStyle +import com.mirego.sample.ui.theming.bold +import com.mirego.sample.ui.theming.medium +import com.mirego.sample.viewmodels.showcase.components.text.TextShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDTextTv + +@ExperimentalTvMaterial3Api +@Composable +fun TextTvShowcaseView(textShowcaseViewModel: TextShowcaseViewModel) { + val viewModel: TextShowcaseViewModel by textShowcaseViewModel.observeAsState() + + val textItems = listOf( + viewModel.largeTitle to SampleTextStyle.largeTitle, + viewModel.title1 to SampleTextStyle.title1, + viewModel.title1Bold to SampleTextStyle.title1.bold(), + viewModel.title2 to SampleTextStyle.title2, + viewModel.title2Bold to SampleTextStyle.title2.bold(), + viewModel.title3 to SampleTextStyle.title3, + viewModel.body to SampleTextStyle.body, + viewModel.bodyMedium to SampleTextStyle.body.medium(), + viewModel.button to SampleTextStyle.button, + viewModel.callout to SampleTextStyle.callout, + viewModel.subheadline to SampleTextStyle.subheadline, + viewModel.footnote to SampleTextStyle.footnote, + viewModel.caption1 to SampleTextStyle.caption1, + viewModel.caption2 to SampleTextStyle.caption2, + viewModel.richText to SampleTextStyle.body, + ) + + TvLazyColumn( + contentPadding = PaddingValues(24.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + items(textItems) { textItem -> + Card( + onClick = { } + ) { + VMDTextTv( + modifier = Modifier.padding(16.dp), + viewModel = textItem.first, + style = textItem.second + ) + } + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ToggleTvShowcaseView.kt b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ToggleTvShowcaseView.kt new file mode 100644 index 00000000..d06c06f6 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/android/src/main/java/com/mirego/sample/ui/tv/showcase/ToggleTvShowcaseView.kt @@ -0,0 +1,255 @@ +package com.mirego.sample.ui.tv.showcase + +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.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.unit.dp +import androidx.tv.foundation.lazy.list.TvLazyRow +import androidx.tv.material3.ExperimentalTvMaterial3Api +import androidx.tv.material3.Text +import com.mirego.sample.ui.theming.SampleTextStyle +import com.mirego.sample.viewmodels.showcase.components.toggle.ToggleShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.compose.extensions.observeAsState +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.LocalImage +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDCheckboxTv +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDSwitchTv +import com.mirego.trikot.viewmodels.declarative.compose.viewmodel.VMDTextTv + +@ExperimentalTvMaterial3Api +@Composable +fun ToggleTvShowcaseView(toggleShowcaseViewModel: ToggleShowcaseViewModel) { + val viewModel: ToggleShowcaseViewModel by toggleShowcaseViewModel.observeAsState() + + Box( + modifier = Modifier + .fillMaxSize() + ) { + TvLazyRow( + modifier = Modifier + .align(Alignment.Center) + .background(MaterialTheme.colorScheme.surfaceVariant), + contentPadding = PaddingValues(24.dp), + horizontalArrangement = Arrangement.spacedBy(24.dp) + ) { + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDCheckboxTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.emptyToggle + ) + VMDTextTv( + viewModel = viewModel.checkboxTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDCheckboxTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textToggle, + label = { content -> + Text( + modifier = Modifier.padding(end = 6.dp), + text = content.text + ) + } + ) + VMDTextTv( + viewModel = viewModel.textCheckboxTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDCheckboxTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.imageToggle, + label = { content -> + LocalImage( + modifier = Modifier.padding(end = 6.dp), + imageResource = content.image, + colorFilter = ColorFilter.tint(Color.Black) + ) + } + ) + VMDTextTv( + viewModel = viewModel.imageCheckboxTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDCheckboxTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textImageToggle, + label = { content -> + Row( + modifier = Modifier.padding(end = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + LocalImage( + imageResource = content.image, + colorFilter = ColorFilter.tint(Color.Black) + ) + Text( + text = content.text, + style = SampleTextStyle.body + ) + } + } + ) + VMDTextTv( + viewModel = viewModel.textImageCheckboxTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDCheckboxTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textPairToggle, + label = { content -> + Column( + modifier = Modifier.padding(end = 6.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = content.first, + style = SampleTextStyle.body + ) + Text( + text = content.second, + style = SampleTextStyle.caption1 + ) + } + } + ) + VMDTextTv( + viewModel = viewModel.textPairCheckboxTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDSwitchTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.emptyToggle + ) + VMDTextTv( + viewModel = viewModel.switchTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDSwitchTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textToggle, + label = { content -> + Text( + content.text, + style = SampleTextStyle.body + ) + } + ) + VMDTextTv( + viewModel = viewModel.textSwitchTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDSwitchTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.imageToggle, + label = { content -> + LocalImage( + modifier = Modifier.padding(end = 6.dp), + imageResource = content.image, + colorFilter = ColorFilter.tint(Color.Black) + ) + } + ) + VMDTextTv( + viewModel = viewModel.imageSwitchTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDSwitchTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textImageToggle, + label = { content -> + Row( + modifier = Modifier.padding(end = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + LocalImage( + imageResource = content.image, + colorFilter = ColorFilter.tint(Color.Black) + ) + Text( + modifier = Modifier.padding(start = 8.dp), + text = content.text, + style = SampleTextStyle.body + ) + } + } + ) + VMDTextTv( + viewModel = viewModel.textImageSwitchTitle + ) + } + } + item { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + VMDSwitchTv( + modifier = Modifier + .padding(16.dp), + viewModel = viewModel.textPairToggle, + label = { content -> + Column( + modifier = Modifier.padding(end = 6.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = content.first, + style = SampleTextStyle.body + ) + Text( + text = content.second, + style = SampleTextStyle.caption1 + ) + } + } + ) + VMDTextTv( + viewModel = viewModel.textPairSwitchTitle + ) + } + } + } + } +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactory.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactory.kt index 02da9e18..b62e5d82 100644 --- a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactory.kt +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactory.kt @@ -11,6 +11,7 @@ import com.mirego.sample.viewmodels.showcase.components.snackbar.SnackbarShowcas import com.mirego.sample.viewmodels.showcase.components.text.TextShowcaseViewModelController import com.mirego.sample.viewmodels.showcase.components.textfield.TextFieldShowcaseViewModelController import com.mirego.sample.viewmodels.showcase.components.toggle.ToggleShowcaseViewModelController +import com.mirego.sample.viewmodels.tv.HomeTvViewModelController import com.mirego.trikot.viewmodels.declarative.controller.VMDViewModelControllerFactory interface SampleViewModelControllerFactory : VMDViewModelControllerFactory { @@ -35,4 +36,6 @@ interface SampleViewModelControllerFactory : VMDViewModelControllerFactory { fun pickerShowcase(): PickerShowcaseViewModelController fun snackbarShowcase(): SnackbarShowcaseViewModelController + + fun homeTv(): HomeTvViewModelController } diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactoryImpl.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactoryImpl.kt index bc865473..84a2ec0d 100644 --- a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactoryImpl.kt +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/factories/SampleViewModelControllerFactoryImpl.kt @@ -11,6 +11,7 @@ import com.mirego.sample.viewmodels.showcase.components.snackbar.SnackbarShowcas import com.mirego.sample.viewmodels.showcase.components.text.TextShowcaseViewModelController import com.mirego.sample.viewmodels.showcase.components.textfield.TextFieldShowcaseViewModelController import com.mirego.sample.viewmodels.showcase.components.toggle.ToggleShowcaseViewModelController +import com.mirego.sample.viewmodels.tv.HomeTvViewModelController import com.mirego.trikot.kword.I18N import com.mirego.trikot.kword.KWord @@ -50,4 +51,6 @@ class SampleViewModelControllerFactoryImpl : SampleViewModelControllerFactory { override fun snackbarShowcase() = SnackbarShowcaseViewModelController(i18N) + + override fun homeTv() = HomeTvViewModelController(i18N) } diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModel.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModel.kt new file mode 100644 index 00000000..aff407ee --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModel.kt @@ -0,0 +1,14 @@ +package com.mirego.sample.viewmodels.showcase.components.carousel + +import com.mirego.sample.viewmodels.showcase.ShowcaseViewModel +import com.mirego.trikot.viewmodels.declarative.components.VMDImageViewModel + +interface CarouselShowcaseViewModel : ShowcaseViewModel { + val items: List +} + +data class CarouselItemContent( + val title: String, + val description: String, + val image: VMDImageViewModel +) diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModelImpl.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModelImpl.kt new file mode 100644 index 00000000..a395387b --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/showcase/components/carousel/CarouselShowcaseViewModelImpl.kt @@ -0,0 +1,30 @@ +package com.mirego.sample.viewmodels.showcase.components.carousel + +import com.mirego.sample.KWordTranslation +import com.mirego.sample.viewmodels.showcase.ShowcaseViewModelImpl +import com.mirego.trikot.kword.I18N +import com.mirego.trikot.viewmodels.declarative.viewmodel.remoteImage +import com.mirego.trikot.viewmodels.declarative.viewmodel.text +import kotlinx.coroutines.CoroutineScope + +class CarouselShowcaseViewModelImpl(i18N: I18N, coroutineScope: CoroutineScope) : ShowcaseViewModelImpl(coroutineScope), CarouselShowcaseViewModel { + override val items: List = listOf( + CarouselItemContent( + title = "Item 1", + description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore" + + " magna aliqua. Blandit aliquam etiam erat velit scelerisque in dictum non.", + image = remoteImage("https://picsum.photos/1280/720") + ), + CarouselItemContent( + title = "Item 2", + description = "Volutpat commodo sed egestas egestas fringilla phasellus faucibus scelerisque eleifend.", + image = remoteImage("https://picsum.photos/1280/721") + ), + CarouselItemContent( + title = "Item 3", + description = "Tellus in hac habitasse platea dictumst. Fermentum leo vel orci porta non pulvinar neque laoreet. Volutpat ac tincidunt vitae semper.", + image = remoteImage("https://picsum.photos/1280/722") + ) + ) + override val title = text(i18N[KWordTranslation.CAROUSEL_SHOWCASE_TITLE]) +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeMenuSectionItem.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeMenuSectionItem.kt new file mode 100644 index 00000000..cda2e9c4 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeMenuSectionItem.kt @@ -0,0 +1,33 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.sample.viewmodels.showcase.components.button.ButtonShowcaseViewModel +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselShowcaseViewModel +import com.mirego.sample.viewmodels.showcase.components.text.TextShowcaseViewModel +import com.mirego.sample.viewmodels.showcase.components.toggle.ToggleShowcaseViewModel + +sealed interface HomeMenuSectionItem : MenuSectionItem { + data class ToggleShowcase( + override val viewModel: ToggleShowcaseViewModel, + override val title: String + ) : HomeMenuSectionItem + + data class TextShowcase( + override val viewModel: TextShowcaseViewModel, + override val title: String + ) : HomeMenuSectionItem + + data class ButtonShowcase( + override val viewModel: ButtonShowcaseViewModel, + override val title: String + ) : HomeMenuSectionItem + + data class CarouselShowcase( + override val viewModel: CarouselShowcaseViewModel, + override val title: String + ) : HomeMenuSectionItem + + data class TopNavigationShowcase( + override val viewModel: CarouselShowcaseViewModel, + override val title: String + ) : HomeMenuSectionItem +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvNavigationDelegate.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvNavigationDelegate.kt new file mode 100644 index 00000000..064740a5 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvNavigationDelegate.kt @@ -0,0 +1,5 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.trikot.viewmodels.declarative.controller.VMDNavigationDelegate + +interface HomeTvNavigationDelegate : VMDNavigationDelegate diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModel.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModel.kt new file mode 100644 index 00000000..76b71d53 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModel.kt @@ -0,0 +1,7 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.trikot.viewmodels.declarative.viewmodel.VMDViewModel + +interface HomeTvViewModel : VMDViewModel { + val menuItems: List +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelController.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelController.kt new file mode 100644 index 00000000..a72f4d24 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelController.kt @@ -0,0 +1,8 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.trikot.kword.I18N +import com.mirego.trikot.viewmodels.declarative.controller.VMDViewModelController + +class HomeTvViewModelController(i18N: I18N) : VMDViewModelController() { + override val viewModel = HomeTvViewModelImpl(i18N, viewModelScope) +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelImpl.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelImpl.kt new file mode 100644 index 00000000..033d9bf8 --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/HomeTvViewModelImpl.kt @@ -0,0 +1,39 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.sample.KWordTranslation +import com.mirego.sample.viewmodels.showcase.components.button.ButtonShowcaseViewModelImpl +import com.mirego.sample.viewmodels.showcase.components.carousel.CarouselShowcaseViewModelImpl +import com.mirego.sample.viewmodels.showcase.components.text.TextShowcaseViewModelImpl +import com.mirego.sample.viewmodels.showcase.components.toggle.ToggleShowcaseViewModelImpl +import com.mirego.trikot.kword.I18N +import com.mirego.trikot.viewmodels.declarative.viewmodel.VMDViewModelImpl +import kotlinx.coroutines.CoroutineScope + +class HomeTvViewModelImpl( + i18N: I18N, + coroutineScope: CoroutineScope +) : HomeTvViewModel, VMDViewModelImpl(coroutineScope) { + + override val menuItems: List = listOf( + HomeMenuSectionItem.TextShowcase( + title = i18N[KWordTranslation.TEXT_SHOWCASE_TITLE], + viewModel = TextShowcaseViewModelImpl(i18N, coroutineScope) + ), + HomeMenuSectionItem.ButtonShowcase( + title = i18N[KWordTranslation.BUTTON_SHOWCASE_TITLE], + viewModel = ButtonShowcaseViewModelImpl(i18N, coroutineScope) + ), + HomeMenuSectionItem.ToggleShowcase( + title = i18N[KWordTranslation.TOGGLE_SHOWCASE_TITLE], + viewModel = ToggleShowcaseViewModelImpl(i18N, coroutineScope) + ), + HomeMenuSectionItem.CarouselShowcase( + title = i18N[KWordTranslation.CAROUSEL_SHOWCASE_TITLE], + viewModel = CarouselShowcaseViewModelImpl(i18N, coroutineScope) + ), + HomeMenuSectionItem.TopNavigationShowcase( + title = i18N[KWordTranslation.TOP_NAVIGATION_SHOWCASE_TITLE], + viewModel = CarouselShowcaseViewModelImpl(i18N, coroutineScope) + ) + ) +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/MenuSectionItem.kt b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/MenuSectionItem.kt new file mode 100644 index 00000000..549678df --- /dev/null +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/kotlin/com/mirego/sample/viewmodels/tv/MenuSectionItem.kt @@ -0,0 +1,9 @@ +package com.mirego.sample.viewmodels.tv + +import com.mirego.trikot.viewmodels.declarative.viewmodel.VMDViewModel + +interface MenuSectionItem { + val viewModel: VMDViewModel? + get() = null + val title: String +} diff --git a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/resources/translations/translation.en.json b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/resources/translations/translation.en.json index da61461a..e2ece7be 100644 --- a/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/resources/translations/translation.en.json +++ b/trikot-viewmodels-declarative-flow/sample/common/src/commonMain/resources/translations/translation.en.json @@ -6,6 +6,7 @@ "button_showcase_image_title": "Image button", "button_showcase_text_image_title": "Text image button", "button_showcase_text_pair_title": "Text pair button", + "carousel_showcase_title": "Carousel showcase", "home_title": "trikot.viewmodels-declarative-flows", "home_components_title": "Components", "home_component_text": "Text", @@ -70,6 +71,7 @@ "toggle_showcase_image_switch_title": "Image switch", "toggle_showcase_text_image_switch_title": "Text image switch", "toggle_showcase_text_pair_switch_title": "Text pair switch", + "top_navigation_showcase_title": "Top navigation showcase", "animation_types_showcase_title": "Animation types", "animation_types_animate_button_text": "Animate", "animation_types_linear_tile": "Linear",