Various fixes for EntriesScreen class

pull/122/head
Arty Bishop 2023-02-18 13:38:04 +00:00
rodzic a0c82df70a
commit a3fd124fed
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 5C71CFDC37AD73CC
9 zmienionych plików z 205 dodań i 111 usunięć

Wyświetl plik

@ -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"

Wyświetl plik

@ -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
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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() }
}
}

Wyświetl plik

@ -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() }

Wyświetl plik

@ -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))
}
}
}
}
}

Wyświetl plik

@ -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>

Wyświetl plik

@ -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>

Wyświetl plik

@ -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