ソースを参照

carousel finalization

Milan Jurkulak 3 年 前
親
コミット
ed49dd87dc

+ 46 - 8
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/carousel/BigCarousel.kt

@@ -10,27 +10,32 @@ package org.mjdev.tvapp.base.ui.components.carousel
 
 import android.annotation.SuppressLint
 import android.content.res.Configuration
+import androidx.compose.foundation.gestures.detectHorizontalDragGestures
 import androidx.compose.foundation.layout.BoxWithConstraints
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusState
 import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.tv.material3.CardScale
 import androidx.tv.material3.Carousel
+import androidx.tv.material3.CarouselState
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import org.mjdev.tvapp.base.extensions.ComposeExt.rememberFocusState
 import org.mjdev.tvapp.base.extensions.ModifierExt.recomposeHighlighter
+import kotlin.math.abs
 
-// todo swipe left and swipe right
 @SuppressLint("AutoboxingStateValueProperty")
 @OptIn(ExperimentalTvMaterial3Api::class)
 @Preview
@@ -38,30 +43,40 @@ import org.mjdev.tvapp.base.extensions.ModifierExt.recomposeHighlighter
 fun BigCarousel(
     modifier: Modifier = Modifier,
     items: List<Any?> = listOf(Unit, Unit, Unit),
+    autoScrollDurationMillis: Long = 500,
+    focusState: MutableState<FocusState?> = rememberFocusState(items),
+    carouselState: MutableState<CarouselState> = remember { mutableStateOf(CarouselState()) },
     onItemSelected: (movie: Any?) -> Unit = {},
     onItemClicked: (movie: Any?) -> Unit = {},
 ) {
     val height = LocalConfiguration.current.let { config ->
-        if (config.orientation == Configuration.ORIENTATION_PORTRAIT) config.screenWidthDp * 0.4f
-        else config.screenHeightDp * 0.4f
+        if (config.orientation == Configuration.ORIENTATION_PORTRAIT)
+            config.screenWidthDp * 0.4f
+        else
+            config.screenHeightDp * 0.4f
     }
-    val focusState: MutableState<FocusState?> = rememberFocusState(items)
-    val itemIndex = remember { mutableIntStateOf(0) }
-    val selectedItem: () -> Any? = { items[itemIndex.value] }
+    val selectedItem: () -> Any? = { items[carouselState.value.activeItemIndex] }
+    val scrollDelta = remember { mutableFloatStateOf(0f) }
     BoxWithConstraints {
         Carousel(
+            autoScrollDurationMillis = autoScrollDurationMillis,
+            carouselState = carouselState.value,
             itemCount = items.size,
             modifier = modifier
                 .recomposeHighlighter()
                 .fillMaxWidth()
                 .height(height.dp),
         ) { indexOfCarouselItem ->
-            itemIndex.value = indexOfCarouselItem
             CarouselCard(
                 item = items[indexOfCarouselItem],
                 modifier = modifier
                     .fillMaxWidth()
                     .height(height.dp)
+                    .pointerInput(Unit) {
+                        detectHorizontalDragGestures { _, dragAmount ->
+                            scrollDelta.value = dragAmount
+                        }
+                    }
                     .onFocusChanged { state ->
                         if (state.isFocused || state.hasFocus) {
                             onItemSelected(selectedItem())
@@ -76,4 +91,27 @@ fun BigCarousel(
             )
         }
     }
+    // todo improve
+    LaunchedEffect(scrollDelta.value) {
+        val activeItemIndex = carouselState.value.activeItemIndex
+        carouselState.value.pauseAutoScroll(activeItemIndex)
+        if(abs(scrollDelta.value) > 250) {
+            if (scrollDelta.value < 0) {
+                val nextItem = carouselState.value.activeItemIndex + 1
+                if (nextItem <= items.size) {
+                    carouselState.value = CarouselState(nextItem).apply {
+                        pauseAutoScroll(nextItem)
+                    }
+                }
+            }
+            if (scrollDelta.value > 0) {
+                val prevItem = carouselState.value.activeItemIndex - 1
+                if (prevItem >= 0) {
+                    carouselState.value = CarouselState(prevItem).apply {
+                        pauseAutoScroll(prevItem)
+                    }
+                }
+            }
+        }
+    }
 }

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/tv/BrowseView.kt

@@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import androidx.tv.material3.ExperimentalTvMaterial3Api
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.base.annotations.TvPreview
 import org.mjdev.tvapp.base.extensions.ComposeExt.isEditMode
@@ -32,6 +33,7 @@ import org.mjdev.tvapp.base.network.NetworkStatus
 import org.mjdev.tvapp.base.network.isNotConnected
 import org.mjdev.tvapp.base.ui.components.carousel.BigCarousel
 
+@OptIn(ExperimentalTvMaterial3Api::class)
 @TvPreview
 @Composable
 fun BrowseView(

+ 3 - 3
app/src/main/kotlin/org/mjdev/tvapp/base/ui/components/tv/CursorRow.kt

@@ -14,7 +14,7 @@ import android.database.Cursor
 import android.net.Uri
 import androidx.compose.foundation.background
 import androidx.compose.foundation.gestures.animateScrollBy
-import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.gestures.detectHorizontalDragGestures
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -95,8 +95,8 @@ fun CursorRow(
                 modifier = Modifier
                     .fillMaxWidth()
                     .pointerInput(Unit) {
-                        detectDragGestures { _, dragAmount ->
-                            scrollDelta.value = dragAmount.x
+                        detectHorizontalDragGestures { _, dragAmount ->
+                            scrollDelta.value = dragAmount
                         }
                     }
                     .recomposeHighlighter(),

+ 1 - 1
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/MainViewModel.kt

@@ -59,7 +59,7 @@ class MainViewModel @Inject constructor(
 
     val featuredMovieList: StateFlow<List<Any?>> = flow {
         mutableListOf<Any?>().apply {
-            add(repository.getMovies().getOrThrow().takeLast(8))
+            addAll(repository.getMovies().getOrThrow().takeLast(8))
             if (localAudioCursor.count > 0) {
                 add(localAudioCursor.getData(0))
             }