kopia lustrzana https://github.com/rt-bishop/Look4Sat
Various fixes for EntriesScreen class
rodzic
a0c82df70a
commit
a3fd124fed
|
@ -12,6 +12,7 @@
|
|||
|
||||
<application
|
||||
android:name=".presentation.MainApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.Interaction
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
|
@ -16,17 +17,16 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
||||
@Composable
|
||||
fun CardButton(onClick: () -> 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<Interaction> = emptyFlow()
|
||||
override suspend fun emit(interaction: Interaction) {}
|
||||
override fun tryEmit(interaction: Interaction) = true
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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<List<SatItem>>, onSelected: (List<Int>, 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<SatItem>, onSelected: (List<Int>, 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<Int>, 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<Int>, 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<List<SatItem>>, onSelected: (List<Int>, 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z" />
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z" />
|
||||
</vector>
|
|
@ -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,10 +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.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
|
||||
|
|
Ładowanie…
Reference in New Issue