[feat] New video UI& Basic Search feature
This commit is contained in:
		| @@ -167,9 +167,7 @@ fun AppNavigation() { | ||||
|                 } | ||||
|             } | ||||
|             composable(Screen.Video.route) { | ||||
|                 CardPage(title = "Videos") { | ||||
|                     VideoScreen(navController = navController) | ||||
|                 } | ||||
|                 VideoScreen(navController = navController) | ||||
|             } | ||||
|             composable(Screen.Comic.route) { | ||||
|                 CardPage(title = "Comic") { | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| package com.acitelight.aether.view | ||||
|  | ||||
| import android.widget.Toast | ||||
| import androidx.compose.animation.AnimatedVisibility | ||||
| import androidx.compose.animation.slideInHorizontally | ||||
| import androidx.compose.animation.slideInVertically | ||||
| import androidx.compose.animation.slideOutHorizontally | ||||
| import androidx.compose.animation.slideOutVertically | ||||
| import androidx.compose.foundation.background | ||||
| import androidx.compose.foundation.clickable | ||||
| import androidx.compose.foundation.combinedClickable | ||||
| @@ -10,19 +15,35 @@ import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.PaddingValues | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxHeight | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.height | ||||
| import androidx.compose.foundation.layout.heightIn | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.size | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.foundation.layout.widthIn | ||||
| import androidx.compose.foundation.layout.wrapContentHeight | ||||
| import androidx.compose.foundation.lazy.LazyColumn | ||||
| import androidx.compose.foundation.lazy.grid.GridCells | ||||
| import androidx.compose.foundation.lazy.grid.LazyVerticalGrid | ||||
| import androidx.compose.foundation.lazy.grid.items | ||||
| import androidx.compose.foundation.lazy.items | ||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||
| import androidx.compose.foundation.text.BasicTextField | ||||
| import androidx.compose.material.icons.Icons | ||||
| import androidx.compose.material.icons.filled.Menu | ||||
| import androidx.compose.material.icons.filled.Search | ||||
| import androidx.compose.material3.Button | ||||
| import androidx.compose.material3.Card | ||||
| import androidx.compose.material3.CardDefaults | ||||
| import androidx.compose.material3.CheckboxDefaults.colors | ||||
| import androidx.compose.material3.DividerDefaults | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.HorizontalDivider | ||||
| import androidx.compose.material3.Icon | ||||
| import androidx.compose.material3.LocalTextStyle | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.Tab | ||||
| import androidx.compose.material3.Text | ||||
| @@ -40,21 +61,33 @@ import coil3.compose.AsyncImage | ||||
| import com.acitelight.aether.model.Video | ||||
| import com.acitelight.aether.viewModel.VideoScreenViewModel | ||||
| import androidx.compose.material3.ScrollableTabRow | ||||
| import androidx.compose.material3.Surface | ||||
| import androidx.compose.material3.TextField | ||||
| import androidx.compose.material3.TextFieldDefaults | ||||
| import androidx.compose.runtime.mutableStateListOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.setValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.graphics.Brush | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.platform.LocalDensity | ||||
| import androidx.compose.ui.text.AnnotatedString | ||||
| import androidx.compose.ui.text.TextStyle | ||||
| import androidx.compose.ui.text.rememberTextMeasurer | ||||
| import androidx.compose.ui.text.style.TextAlign | ||||
| import androidx.compose.ui.unit.min | ||||
| import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import androidx.navigation.NavHostController | ||||
| import coil3.request.ImageRequest | ||||
| import com.acitelight.aether.CardPage | ||||
| import com.acitelight.aether.Global | ||||
| import com.acitelight.aether.Global.updateRelate | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import java.nio.charset.Charset | ||||
| import java.security.KeyPair | ||||
|  | ||||
| fun String.toHex(): String { | ||||
|     return this.toByteArray().joinToString("") { "%02x".format(it) } | ||||
| @@ -76,32 +109,176 @@ fun VideoScreen( | ||||
|     videoScreenViewModel: VideoScreenViewModel = hiltViewModel<VideoScreenViewModel>(), | ||||
|     navController: NavHostController | ||||
| ) { | ||||
|     val tabIndex by videoScreenViewModel.tabIndex; | ||||
|     val colorScheme = MaterialTheme.colorScheme | ||||
|     val tabIndex by videoScreenViewModel.tabIndex | ||||
|     var menuVisibility by videoScreenViewModel.menuVisibility | ||||
|     var searchFilter by videoScreenViewModel.searchFilter | ||||
|  | ||||
|     Column( | ||||
|         modifier = Modifier.fillMaxSize() | ||||
|     ) { | ||||
|         TopRow(videoScreenViewModel); | ||||
|  | ||||
|         LazyVerticalGrid( | ||||
|             columns = GridCells.Adaptive(160.dp), | ||||
|             contentPadding = PaddingValues(8.dp), | ||||
|             verticalArrangement = Arrangement.spacedBy(8.dp), | ||||
|             horizontalArrangement = Arrangement.spacedBy(8.dp) | ||||
|         ) | ||||
|     CardPage(title = "Videos") { | ||||
|         Box(Modifier.fillMaxSize()) | ||||
|         { | ||||
|             if (videoScreenViewModel.videoLibrary.classes.isNotEmpty()) { | ||||
|                 items( | ||||
|                     videoScreenViewModel.videoLibrary.classesMap[videoScreenViewModel.videoLibrary.classes[tabIndex]] | ||||
|                         ?: mutableStateListOf() | ||||
|                 ) { video -> | ||||
|                     VideoCard(video, navController, videoScreenViewModel) | ||||
|             Column( | ||||
|                 modifier = Modifier.fillMaxSize() | ||||
|             ) { | ||||
|                 // TopRow(videoScreenViewModel); | ||||
|                 Row(Modifier.padding(bottom = 4.dp)) | ||||
|                 { | ||||
|                     Card( | ||||
|                         shape = RoundedCornerShape(8.dp), | ||||
|                         colors = CardDefaults.cardColors(containerColor = colorScheme.primary), | ||||
|                         modifier = Modifier | ||||
|                             .align(Alignment.CenterVertically) | ||||
|                             .padding(horizontal = 2.dp) | ||||
|                             .size(36.dp), | ||||
|                         onClick = { | ||||
|                             menuVisibility = !menuVisibility | ||||
|                         }) | ||||
|                     { | ||||
|                         Box(Modifier.fillMaxSize()) | ||||
|                         { | ||||
|                             Icon( | ||||
|                                 modifier = Modifier | ||||
|                                     .size(30.dp) | ||||
|                                     .align(Alignment.Center), | ||||
|                                 imageVector = Icons.Default.Menu, | ||||
|                                 contentDescription = "Catalogue" | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     Card( | ||||
|                         shape = RoundedCornerShape(8.dp), | ||||
|                         colors = CardDefaults.cardColors(containerColor = colorScheme.primary), | ||||
|                         modifier = Modifier | ||||
|                             .align(Alignment.CenterVertically) | ||||
|                             .padding(horizontal = 2.dp) | ||||
|                             .height(36.dp), | ||||
|                         onClick = { | ||||
|                             menuVisibility = !menuVisibility | ||||
|                         }) | ||||
|                     { | ||||
|                         Box(Modifier.fillMaxHeight()) | ||||
|                         { | ||||
|                             Text( | ||||
|                                 text = videoScreenViewModel.videoLibrary.classes.getOrNull(tabIndex) | ||||
|                                     ?: "", | ||||
|                                 style = MaterialTheme.typography.bodyLarge, | ||||
|                                 fontWeight = FontWeight.Bold, | ||||
|                                 modifier = Modifier | ||||
|                                     .align(Alignment.CenterStart) | ||||
|                                     .padding(horizontal = 8.dp), | ||||
|                                 maxLines = 1 | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     Box( | ||||
|                         modifier = Modifier | ||||
|                             .height(36.dp).widthIn(max = 240.dp) | ||||
|                             .background(colorScheme.primary, RoundedCornerShape(8.dp)) | ||||
|                             .padding(horizontal = 6.dp), | ||||
|                         contentAlignment = Alignment.CenterStart | ||||
|                     ) { | ||||
|                         BasicTextField( | ||||
|                             value = searchFilter, | ||||
|                             onValueChange = { searchFilter = it }, | ||||
|                             textStyle = LocalTextStyle.current.copy( | ||||
|                                 fontSize = 18.sp, | ||||
|                                 color = Color.White, | ||||
|                                 textAlign = TextAlign.Start | ||||
|                             ), | ||||
|                             singleLine = true, | ||||
|                             modifier = Modifier | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|                 HorizontalDivider(Modifier.padding(bottom = 8.dp), 1.5.dp, DividerDefaults.color) | ||||
|                 LazyVerticalGrid( | ||||
|                     columns = GridCells.Adaptive(160.dp), | ||||
|                     contentPadding = PaddingValues(8.dp), | ||||
|                     verticalArrangement = Arrangement.spacedBy(8.dp), | ||||
|                     horizontalArrangement = Arrangement.spacedBy(8.dp) | ||||
|                 ) | ||||
|                 { | ||||
|                     items( | ||||
|                         videoScreenViewModel.videoLibrary.classesMap.getOrDefault( | ||||
|                             videoScreenViewModel.videoLibrary.classes.getOrNull( | ||||
|                                 tabIndex | ||||
|                             ), listOf() | ||||
|                         ).filter { it.video.name.contains(searchFilter) } | ||||
|                     ) { video -> | ||||
|                         VideoCard(video, navController, videoScreenViewModel) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             AnimatedVisibility( | ||||
|                 visible = menuVisibility, | ||||
|                 enter = slideInHorizontally(initialOffsetX = { full -> full }), | ||||
|                 exit = slideOutHorizontally(targetOffsetX = { full -> full }), | ||||
|                 modifier = Modifier.align(Alignment.CenterEnd) | ||||
|             ) { | ||||
|                 Card( | ||||
|                     Modifier | ||||
|                         .fillMaxHeight() | ||||
|                         .width(200.dp) | ||||
|                         .align(Alignment.CenterEnd), | ||||
|                     shape = RoundedCornerShape(8.dp), | ||||
|                     colors = CardDefaults.cardColors(containerColor = colorScheme.surface) | ||||
|                 ) | ||||
|                 { | ||||
|                     LazyColumn { | ||||
|                         items(videoScreenViewModel.videoLibrary.classes) { item -> | ||||
|                             CatalogueItemRow( | ||||
|                                 item = Pair( | ||||
|                                     videoScreenViewModel.videoLibrary.classes.indexOf(item), | ||||
|                                     item | ||||
|                                 ), | ||||
|                                 onItemClick = { | ||||
|                                     menuVisibility = false | ||||
|                                     videoScreenViewModel.setTabIndex( | ||||
|                                         videoScreenViewModel.videoLibrary.classes.indexOf( | ||||
|                                             item | ||||
|                                         ) | ||||
|                                     ) | ||||
|                                 } | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Composable | ||||
| fun CatalogueItemRow( | ||||
|     item: Pair<Int, String>, | ||||
|     onItemClick: (Pair<Int, String>) -> Unit | ||||
| ) { | ||||
|     val colorScheme = MaterialTheme.colorScheme | ||||
|     Card( | ||||
|         modifier = Modifier | ||||
|             .clickable { onItemClick(item) } | ||||
|             .padding(4.dp) | ||||
|             .padding(horizontal = 4.dp) | ||||
|             .heightIn(min = 28.dp) | ||||
|             .width(200.dp), | ||||
|         shape = RoundedCornerShape(8.dp), | ||||
|         colors = CardDefaults.cardColors(containerColor = colorScheme.primary) | ||||
|     ) { | ||||
|         Text( | ||||
|             text = item.second, | ||||
|             fontSize = 18.sp, | ||||
|             maxLines = 1, | ||||
|             textAlign = TextAlign.Center, | ||||
|             modifier = Modifier | ||||
|                 .fillMaxWidth() | ||||
|                 .padding(horizontal = 8.dp, vertical = 4.dp) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @OptIn(ExperimentalMaterial3Api::class) | ||||
| @Composable | ||||
| fun TopRow(videoScreenViewModel: VideoScreenViewModel) { | ||||
| @@ -200,10 +377,12 @@ fun VideoCard( | ||||
|                 ) | ||||
|  | ||||
|                 if (video.isLocal) | ||||
|                     Card(Modifier | ||||
|                         .align(Alignment.TopStart) | ||||
|                         .padding(5.dp) | ||||
|                         .widthIn(max = 46.dp)) { | ||||
|                     Card( | ||||
|                         Modifier | ||||
|                             .align(Alignment.TopStart) | ||||
|                             .padding(5.dp) | ||||
|                             .widthIn(max = 46.dp) | ||||
|                     ) { | ||||
|                         Box(Modifier.fillMaxWidth()) | ||||
|                         { | ||||
|                             Text( | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import androidx.compose.runtime.State | ||||
| import androidx.compose.runtime.mutableIntStateOf | ||||
| import androidx.compose.runtime.mutableStateListOf | ||||
| import androidx.compose.runtime.mutableStateMapOf | ||||
| import androidx.compose.runtime.mutableStateOf | ||||
| import androidx.compose.runtime.snapshots.SnapshotStateList | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.lifecycle.ViewModel | ||||
| @@ -44,6 +45,8 @@ class VideoScreenViewModel @Inject constructor( | ||||
|     private val _tabIndex = mutableIntStateOf(0) | ||||
|     val tabIndex: State<Int> = _tabIndex | ||||
|     var imageLoader: ImageLoader? = null; | ||||
|     var menuVisibility = mutableStateOf(false) | ||||
|     var searchFilter = mutableStateOf("") | ||||
|  | ||||
|     suspend fun init() { | ||||
|         fetchManager.configured.filter { it }.first() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 acite
					acite