From f13662747cf725cfa4a10b3537db0515f32fda06 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Sat, 18 Feb 2023 13:38:04 +0000 Subject: [PATCH] Various fixes for EntriesScreen class --- app/src/main/AndroidManifest.xml | 1 + .../look4sat/presentation/MainComponents.kt | 16 +- .../look4sat/presentation/MainTheme.kt | 8 +- .../presentation/aboutScreen/AboutScreen.kt | 9 +- .../presentation/bottomNav/BottomNavBar.kt | 5 +- .../entriesScreen/EntriesScreen.kt | 239 +++++++++++------- app/src/main/res/drawable/ic_checkbox_off.xml | 10 + app/src/main/res/drawable/ic_checkbox_on.xml | 10 + gradle.properties | 20 +- 9 files changed, 205 insertions(+), 113 deletions(-) create mode 100644 app/src/main/res/drawable/ic_checkbox_off.xml create mode 100644 app/src/main/res/drawable/ic_checkbox_on.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5b613f50..c95173dc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,6 +12,7 @@ Unit, text: String, modifier: Modifier = Modifier) { ElevatedButton( - onClick = { onClick() }, - colors = ButtonDefaults.buttonColors( + onClick = { onClick() }, colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.tertiary, contentColor = MaterialTheme.colorScheme.onTertiary - ), - shape = MaterialTheme.shapes.medium, - modifier = modifier.padding(start = 4.dp, end = 4.dp) + ), shape = MaterialTheme.shapes.small, modifier = modifier.padding(start = 4.dp, end = 4.dp) ) { Text(text = text, fontSize = 17.sp) } } @@ -37,3 +37,9 @@ fun gotoUrl(context: Context, url: String) { fun Modifier.onClick(onClick: () -> Unit): Modifier = composed { clickable(remember { MutableInteractionSource() }, null) { onClick() } } + +class NoRippleInteractionSource : MutableInteractionSource { + override val interactions: Flow = emptyFlow() + override suspend fun emit(interaction: Interaction) {} + override fun tryEmit(interaction: Interaction) = true +} diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt index 57c14413..435fc1ad 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainTheme.kt @@ -77,11 +77,9 @@ private val Typography = Typography( ) private val Shapes = Shapes( - extraSmall = RoundedCornerShape(4.dp), - small = RoundedCornerShape(6.dp), - medium = RoundedCornerShape(8.dp), - large = RoundedCornerShape(12.dp), - extraLarge = RoundedCornerShape(16.dp) + extraSmall = RoundedCornerShape(4.dp), small = RoundedCornerShape(6.dp), // buttons corners + medium = RoundedCornerShape(8.dp), // cards corners + large = RoundedCornerShape(12.dp), extraLarge = RoundedCornerShape(16.dp) ) @Composable diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/aboutScreen/AboutScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/aboutScreen/AboutScreen.kt index 60e89110..4d7bab70 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/aboutScreen/AboutScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/aboutScreen/AboutScreen.kt @@ -1,6 +1,7 @@ package com.rtbishop.look4sat.presentation.aboutScreen import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.ElevatedCard import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -28,9 +29,11 @@ private const val FDROID_URL = "https://f-droid.org/en/packages/com.rtbishop.loo @Composable fun AboutScreen() { - Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { - CardAbout(BuildConfig.VERSION_NAME) - CardCredits() + LazyColumn( + modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + item { CardAbout(BuildConfig.VERSION_NAME) } + item { CardCredits() } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/bottomNav/BottomNavBar.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/bottomNav/BottomNavBar.kt index 59596653..930d0ed6 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/bottomNav/BottomNavBar.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/bottomNav/BottomNavBar.kt @@ -29,7 +29,7 @@ fun BottomNavBar(navController: NavController) { BottomNavItem.Settings, BottomNavItem.About ) - NavigationBar(modifier = Modifier.height(52.dp)) { + NavigationBar(modifier = Modifier.height(48.dp)) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route items.forEach { item -> @@ -48,8 +48,9 @@ fun BottomNavBar(navController: NavController) { @Composable fun NavigationGraph(navController: NavHostController) { + val navToPasses = { navController.navigate(BottomNavItem.Passes.screen_route) } NavHost(navController, startDestination = BottomNavItem.Passes.screen_route) { - composable(BottomNavItem.Satellites.screen_route) { EntriesScreen() } + composable(BottomNavItem.Satellites.screen_route) { EntriesScreen(navToPasses) } composable(BottomNavItem.Passes.screen_route) { PassesScreen() } composable(BottomNavItem.WorldMap.screen_route) { WorldMapScreen() } composable(BottomNavItem.Settings.screen_route) { SettingsScreen() } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt index 16477a3e..96b86455 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -29,77 +30,32 @@ import com.rtbishop.look4sat.domain.model.DataState import com.rtbishop.look4sat.domain.model.SatItem import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.onClick +import com.rtbishop.look4sat.presentation.passesScreen.PassesViewModel @Composable -fun EntriesScreen(viewModel: EntriesViewModel = hiltViewModel()) { - val dataState = viewModel.satData.observeAsState(DataState.Loading) +fun EntriesScreen(navToPasses: () -> Unit, viewModel: EntriesViewModel = hiltViewModel()) { + val state = viewModel.satData.observeAsState(DataState.Loading) + val passesViewModel: PassesViewModel = hiltViewModel() + val unselectAll = { viewModel.selectCurrentItems(false) } + val selectAll = { viewModel.selectCurrentItems(true) } Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { - Card { SearchBar(setQuery = { newQuery -> viewModel.setQuery(newQuery) }) } - EntryType(entryType = "All") - Card(modifier = Modifier.fillMaxWidth()) { - LoadingOrList(dataState.value) { list, value -> viewModel.updateSelection(list, value) } - } + TopBar(setQuery = { newQuery: String -> viewModel.setQuery(newQuery) }, saveSelection = { + val entries = viewModel.saveSelection() + passesViewModel.calculatePasses(selection = entries) + navToPasses() + }) + MiddleBar(unselectAll = { unselectAll() }, selectAll = { selectAll() }) + EntriesCard(state = state.value) { list, value -> viewModel.updateSelection(list, value) } } } @Preview(showBackground = true) @Composable -private fun EntryTypeBarPreview() { - MainTheme { EntryType(entryType = "Amateur") } -} +private fun TopBarPreview() = MainTheme { TopBar({}, {}) } +@Preview(showBackground = true) @Composable -private fun EntryType(entryType: String, modifier: Modifier = Modifier) { - ElevatedCard(modifier.height(48.dp)) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) { - Icon( - imageVector = Icons.Outlined.PlayArrow, - contentDescription = null, - modifier = modifier.size(32.dp) - ) - Text( - text = "Selected type: $entryType", - fontSize = 18.sp, - fontWeight = FontWeight.Medium, - modifier = modifier.padding(start = 6.dp, end = 6.dp, bottom = 2.dp) - ) - } - } -} - -@Composable -private fun LoadingOrList( - state: DataState>, onSelected: (List, Boolean) -> Unit -) { - when (state) { - is DataState.Loading -> { - LoadingProgress() - } - is DataState.Success -> { - EntriesList(entries = state.data) { list, value -> onSelected(list, value) } - } - else -> { - LoadingProgress() - } - } -} - -@Composable -private fun LoadingProgress() { - Row( - horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically - ) { CircularProgressIndicator(modifier = Modifier.size(64.dp)) } -} - -@OptIn(ExperimentalFoundationApi::class) -@Composable -private fun EntriesList(entries: List, onSelected: (List, Boolean) -> Unit) { - LazyColumn { - items(items = entries, key = { item -> item.catnum }) { entry -> - Entry(entry, onSelected, Modifier.animateItemPlacement()) - } - } -} +private fun MiddleBarPreview() = MainTheme { MiddleBar({}, {}) } @Preview(showBackground = true) @Composable @@ -108,39 +64,23 @@ private fun EntryPreview() { MainTheme { Entry(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) } } +@Preview(showBackground = true) @Composable -private fun Entry(item: SatItem, onSelected: (List, Boolean) -> Unit, modifier: Modifier) { - Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { - Surface(modifier = Modifier.padding(bottom = 1.dp)) { - Row(verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .padding(start = 0.dp, top = 6.dp, end = 12.dp, bottom = 6.dp) - .onClick { onSelected(listOf(item.catnum), item.isSelected.not()) }) { - Text( - text = "Id:${item.catnum} - ", - modifier = Modifier.width(100.dp), - textAlign = TextAlign.End, - color = MaterialTheme.colorScheme.secondary - ) - Text( - text = item.name, - modifier = Modifier - .weight(1f) - .padding(end = 8.dp), - fontWeight = FontWeight.Medium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Checkbox(checked = item.isSelected, onCheckedChange = null) - } - } +private fun EntriesPreview() { + MainTheme { EntriesCard(DataState.Loading) { _, _ -> run {} } } +} + +@Composable +private fun TopBar(setQuery: (String) -> Unit, saveSelection: () -> Unit) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(56.dp)) { + SearchBar(setQuery = { setQuery(it) }, modifier = Modifier.weight(1f)) + SaveButton(saveSelection = { saveSelection() }, modifier = Modifier.height(56.dp)) } } @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun SearchBar(setQuery: (String) -> Unit) { +private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier) { val currentQuery = rememberSaveable { mutableStateOf("") } OutlinedTextField(value = currentQuery.value, onValueChange = { newValue -> @@ -159,10 +99,123 @@ private fun SearchBar(setQuery: (String) -> Unit) { setQuery("") }) }, -// colors = TextFieldDefaults.outlinedTextFieldColors( -// focusedBorderColor = MaterialTheme.colorScheme.onBackground, -// unfocusedBorderColor = MaterialTheme.colorScheme.onBackground -// ), - modifier = Modifier.background(MaterialTheme.colorScheme.surface) + shape = MaterialTheme.shapes.medium, + colors = TextFieldDefaults.outlinedTextFieldColors( + containerColor = MaterialTheme.colorScheme.surface, + focusedBorderColor = MaterialTheme.colorScheme.onSurfaceVariant, + unfocusedBorderColor = MaterialTheme.colorScheme.onSurfaceVariant + ), + modifier = modifier ) } + +@Composable +private fun SaveButton(saveSelection: () -> Unit, modifier: Modifier = Modifier) { + ElevatedButton( + onClick = { saveSelection() }, colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ), shape = MaterialTheme.shapes.medium, modifier = modifier.width(96.dp) + ) { Icon(painter = painterResource(id = R.drawable.ic_done), contentDescription = null) } +} + +@Composable +private fun MiddleBar(unselectAll: () -> Unit, selectAll: () -> Unit) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) { + EntryTypeCard(type = "Amateur", modifier = Modifier.weight(1f)) + SelectionCard(unselectAll = { unselectAll() }, selectAll = { selectAll() }) + } +} + +@Composable +private fun EntryTypeCard(type: String, modifier: Modifier = Modifier) { + ElevatedCard(modifier = modifier) { + Row( + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.height(48.dp) + ) { + Icon( + imageVector = Icons.Outlined.PlayArrow, + contentDescription = null, + modifier = Modifier + .size(36.dp) + .padding(start = 6.dp) + ) + Text( + text = "Type: $type", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + modifier = modifier.padding(start = 16.dp, end = 6.dp, bottom = 2.dp) + ) + } + } +} + +@Composable +private fun SelectionCard(unselectAll: () -> Unit, selectAll: () -> Unit) { + ElevatedCard { + Row(modifier = Modifier.width(96.dp)) { + val unselectIcon = painterResource(id = R.drawable.ic_checkbox_off) + val selectIcon = painterResource(id = R.drawable.ic_checkbox_on) + val iconColor = MaterialTheme.colorScheme.onSurface + IconButton(onClick = { unselectAll() }) { + Icon(painter = unselectIcon, contentDescription = null, tint = iconColor) + } + IconButton(onClick = { selectAll() }) { + Icon(painter = selectIcon, contentDescription = null, tint = iconColor) + } + } + } +} + +@Composable +private fun Entry(item: SatItem, onSelected: (List, Boolean) -> Unit, modifier: Modifier) { + Surface(color = MaterialTheme.colorScheme.background, modifier = modifier) { + Surface(modifier = Modifier.padding(bottom = 1.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(start = 0.dp, top = 8.dp, end = 12.dp, bottom = 8.dp) + .onClick { onSelected(listOf(item.catnum), item.isSelected.not()) }) { + Text( + text = "Id:${item.catnum} - ", + modifier = Modifier.width(104.dp), + textAlign = TextAlign.End, + color = MaterialTheme.colorScheme.secondary + ) + Text( + text = item.name, + modifier = Modifier + .weight(1f) + .padding(end = 6.dp), + fontWeight = FontWeight.Medium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Checkbox(checked = item.isSelected, onCheckedChange = null) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun EntriesCard(state: DataState>, onSelected: (List, Boolean) -> Unit) { + ElevatedCard(modifier = Modifier.fillMaxSize()) { + when (state) { + is DataState.Success -> { + LazyColumn { + items(items = state.data, key = { item -> item.catnum }) { entry -> + Entry(entry, onSelected, Modifier.animateItemPlacement()) + } + } + } + else -> { + Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) { + CircularProgressIndicator(modifier = Modifier.size(80.dp)) + } + } + } + } +} diff --git a/app/src/main/res/drawable/ic_checkbox_off.xml b/app/src/main/res/drawable/ic_checkbox_off.xml new file mode 100644 index 00000000..1547143b --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_checkbox_on.xml b/app/src/main/res/drawable/ic_checkbox_on.xml new file mode 100644 index 00000000..e3b3dc09 --- /dev/null +++ b/app/src/main/res/drawable/ic_checkbox_on.xml @@ -0,0 +1,10 @@ + + + diff --git a/gradle.properties b/gradle.properties index cccbfe6f..cc04f364 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +#org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects @@ -14,12 +14,22 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true +#android.useAndroidX=true # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official +#kotlin.code.style=official # Enables namespacing of each library's R class so that its R class includes only the # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library +#android.nonTransitiveRClass=true +android.enableJetifier=false android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=false +android.useAndroidX=true +kapt.include.compile.classpath=true +kapt.incremental.apt=true +kapt.use.worker.api=true +kotlin.code.style=official +kotlin.daemon.jvm.options=-Xmx4096M +kotlin.incremental=true +org.gradle.caching=true +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g +org.gradle.parallel=true