Added fixes after switching to Material3

pull/122/head
Arty Bishop 2023-02-17 12:27:55 +00:00
rodzic 4f62e9d655
commit 450f54ca36
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 5C71CFDC37AD73CC
7 zmienionych plików z 201 dodań i 147 usunięć

Wyświetl plik

@ -27,11 +27,6 @@ import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.IdRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
@ -59,10 +54,6 @@ class MainActivity : ComponentActivity() {
}
}
fun Modifier.onClick(onClick: () -> Unit): Modifier = composed {
clickable(remember { MutableInteractionSource() }, null) { onClick() }
}
fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
val backStackEntry = findNavController().getBackStackEntry(id)
val observer = LifecycleEventObserver { _, event ->

Wyświetl plik

@ -0,0 +1,39 @@
package com.rtbishop.look4sat.presentation
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ElevatedButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun CardButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier) {
ElevatedButton(
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)
) { Text(text = text, fontSize = 17.sp) }
}
fun gotoUrl(context: Context, url: String) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
fun Modifier.onClick(onClick: () -> Unit): Modifier = composed {
clickable(remember { MutableInteractionSource() }, null) { onClick() }
}

Wyświetl plik

@ -1,34 +1,57 @@
package com.rtbishop.look4sat.presentation
import androidx.compose.material3.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Typography
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
private val Yellow = Color(0xFFFFE082)
private val AccentYellow = Color(0xFFFFE082)
private val TextWhite = Color(0xCCFFFFFF)
private val TextGrey = Color(0x66FFFFFF)
private val TextBlack = Color(0xFF000000)
private val ButtonGrey = Color(0xFF2A2A2A)
private val SurfaceGrey = Color(0xFF1C1C1C)
private val Background = Color(0xFF121212)
private val SurfaceBtn = Color(0xFF2A2A2A)
private val SurfaceCard = Color(0xFF1C1C1C)
private val SurfaceBg = Color(0xFF121212)
private val Transparent = Color(0x00000000)
private val DarkColors = lightColorScheme(
primary = SurfaceGrey,
secondary = Yellow,
tertiary = ButtonGrey,
background = Background,
surface = SurfaceGrey,
onPrimary = TextWhite,
private val MainColors = lightColorScheme(
primary = AccentYellow,
onPrimary = TextBlack,
// primaryContainer = primaryContainer,
// onPrimaryContainer = onPrimaryContainer,
// inversePrimary = inversePrimary,
secondary = AccentYellow,
onSecondary = TextBlack,
secondaryContainer = AccentYellow, // navBar indicator
onSecondaryContainer = TextBlack, // navBar active icon
tertiary = SurfaceBtn,
onTertiary = TextWhite,
// tertiaryContainer = tertiaryContainer,
// onTertiaryContainer = onTertiaryContainer,
background = SurfaceBg,
onBackground = TextWhite,
surface = SurfaceCard,
onSurface = TextWhite,
surfaceTint = SurfaceGrey
surfaceVariant = SurfaceCard,
onSurfaceVariant = TextGrey, // navBar inactive icon
surfaceTint = Transparent,
// inverseSurface = inverseSurface,
// inverseOnSurface = inverseOnSurface,
// error = error,
// onError = onError,
// errorContainer = errorContainer,
// onErrorContainer = onErrorContainer,
// outline = outline,
// outlineVariant = outlineVariant,
// scrim = scrim,
)
private val Typography = Typography(
@ -53,8 +76,12 @@ private val Typography = Typography(
)
)
val Shapes = Shapes(
private val Shapes = Shapes(
extraSmall = RoundedCornerShape(4.dp),
small = RoundedCornerShape(6.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(12.dp),
extraLarge = RoundedCornerShape(16.dp)
)
@Composable
@ -71,7 +98,7 @@ fun MainTheme(
// darkTheme -> DarkColorScheme
// else -> LightColorScheme
// }
val colors = DarkColors
val colors = MainColors
// val view = LocalView.current
// if (!view.isInEditMode) {
// SideEffect {

Wyświetl plik

@ -1,39 +1,36 @@
package com.rtbishop.look4sat.presentation.aboutScreen
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.*
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.CardButton
import com.rtbishop.look4sat.presentation.gotoUrl
private const val POLICY_URL = "https://sites.google.com/view/look4sat-privacy-policy/home"
private const val LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html"
private const val GITHUB_URL = "https://github.com/rt-bishop/Look4Sat/"
private const val SUPPORT_URL = "https://ko-fi.com/rt_bishop"
private const val FDROID_URL = "https://f-droid.org/en/packages/com.rtbishop.look4sat/"
@Preview(showBackground = true, widthDp = 400, heightDp = 720, showSystemUi = true)
@Composable
fun AboutScreen() {
MainTheme {
LazyColumn(
contentPadding = PaddingValues(6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier.fillMaxSize()
) {
item { CardAbout(BuildConfig.VERSION_NAME) }
item { CardCredits() }
}
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
CardAbout(BuildConfig.VERSION_NAME)
CardCredits()
}
}
@ -58,30 +55,25 @@ private fun CardAbout(version: String, modifier: Modifier = Modifier) {
color = MaterialTheme.colorScheme.secondary
)
Text(
text = stringResource(id = R.string.app_version, version), fontSize = 21.sp
text = stringResource(id = R.string.app_version, version), fontSize = 20.sp
)
}
}
Text(
text = stringResource(id = R.string.app_subtitle),
fontSize = 21.sp,
fontSize = 20.sp,
modifier = modifier.padding(top = 4.dp)
)
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.padding(start = 4.dp, end = 4.dp)
horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.padding(4.dp)
) {
LinkButton(
onClick = {
gotoUrl(
context, "https://sites.google.com/view/look4sat-privacy-policy/home"
)
},
CardButton(
onClick = { gotoUrl(context, POLICY_URL) },
text = stringResource(id = R.string.btn_privacy),
modifier = Modifier.weight(1f)
)
LinkButton(
onClick = { gotoUrl(context, "https://www.gnu.org/licenses/gpl-3.0.html") },
CardButton(
onClick = { gotoUrl(context, LICENSE_URL) },
text = stringResource(id = R.string.btn_license),
modifier = Modifier.weight(1f)
)
@ -98,6 +90,7 @@ private fun CardCredits(modifier: Modifier = Modifier) {
Text(
text = stringResource(id = R.string.outro_title),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.secondary,
modifier = modifier.padding(8.dp)
)
@ -105,34 +98,30 @@ private fun CardCredits(modifier: Modifier = Modifier) {
text = stringResource(id = R.string.outro_thanks),
fontSize = 16.sp,
textAlign = TextAlign.Center,
modifier = modifier.padding(start = 8.dp, end = 8.dp)
modifier = modifier.padding(horizontal = 6.dp, vertical = 4.dp)
)
Text(
text = stringResource(id = R.string.outro_license),
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.secondary,
modifier = modifier.padding(start = 8.dp, end = 8.dp, top = 8.dp, bottom = 4.dp)
modifier = modifier.padding(8.dp)
)
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.padding(start = 4.dp, end = 4.dp)
horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.padding(4.dp)
) {
LinkButton(
onClick = { gotoUrl(context, "https://github.com/rt-bishop/Look4Sat/") },
CardButton(
onClick = { gotoUrl(context, GITHUB_URL) },
text = stringResource(id = R.string.btn_github),
modifier = Modifier.weight(1f)
)
LinkButton(
onClick = { gotoUrl(context, "https://ko-fi.com/rt_bishop") },
CardButton(
onClick = { gotoUrl(context, SUPPORT_URL) },
text = stringResource(id = R.string.btn_support),
modifier = Modifier.weight(1f)
)
LinkButton(
onClick = {
gotoUrl(
context, "https://f-droid.org/en/packages/com.rtbishop.look4sat/"
)
},
CardButton(
onClick = { gotoUrl(context, FDROID_URL) },
text = stringResource(id = R.string.btn_fdroid),
modifier = Modifier.weight(1f)
)
@ -140,18 +129,3 @@ private fun CardCredits(modifier: Modifier = Modifier) {
}
}
}
@Composable
private fun LinkButton(onClick: () -> Unit, text: String, modifier: Modifier = Modifier) {
Button(
onClick = { onClick() },
colors = ButtonDefaults.buttonColors(MaterialTheme.colorScheme.tertiary),
modifier = modifier.padding(4.dp)
) {
Text(text = text, style = TextStyle(fontWeight = FontWeight.Bold), fontSize = 18.sp)
}
}
private fun gotoUrl(context: Context, url: String) {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}

Wyświetl plik

@ -1,6 +1,8 @@
package com.rtbishop.look4sat.presentation.bottomNav
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -16,7 +18,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.aboutScreen.AboutScreen
import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreenView
import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreen
@Composable
fun BottomNavBar(navController: NavController) {
@ -27,7 +29,7 @@ fun BottomNavBar(navController: NavController) {
BottomNavItem.Settings,
BottomNavItem.About
)
NavigationBar(modifier = Modifier.height(56.dp)) {
NavigationBar(modifier = Modifier.height(52.dp)) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
@ -47,7 +49,7 @@ fun BottomNavBar(navController: NavController) {
@Composable
fun NavigationGraph(navController: NavHostController) {
NavHost(navController, startDestination = BottomNavItem.Passes.screen_route) {
composable(BottomNavItem.Satellites.screen_route) { EntriesScreenView() }
composable(BottomNavItem.Satellites.screen_route) { EntriesScreen() }
composable(BottomNavItem.Passes.screen_route) { PassesScreen() }
composable(BottomNavItem.WorldMap.screen_route) { WorldMapScreen() }
composable(BottomNavItem.Settings.screen_route) { SettingsScreen() }
@ -57,11 +59,9 @@ fun NavigationGraph(navController: NavHostController) {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreenView() {
val navController = rememberNavController()
Scaffold(bottomBar = { BottomNavBar(navController = navController) }) {
NavigationGraph(navController = navController)
it.calculateBottomPadding()
fun MainScreenView(navController: NavHostController = rememberNavController()) {
Scaffold(bottomBar = { BottomNavBar(navController = navController) }) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) { NavigationGraph(navController) }
}
}

Wyświetl plik

@ -3,12 +3,11 @@ package com.rtbishop.look4sat.presentation.entriesScreen
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
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.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowForward
import androidx.compose.material.icons.outlined.Close
import androidx.compose.material.icons.outlined.PlayArrow
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
@ -18,51 +17,60 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import com.rtbishop.look4sat.R
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
@Composable
fun EntriesScreenView() {
val viewModel = hiltViewModel<EntriesViewModel>()
fun EntriesScreen(viewModel: EntriesViewModel = hiltViewModel()) {
val dataState = viewModel.satData.observeAsState(DataState.Loading)
Column(modifier = Modifier.padding(6.dp)) {
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
Card { SearchBar(setQuery = { newQuery -> viewModel.setQuery(newQuery) }) }
Spacer(modifier = Modifier.height(6.dp))
Card(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) {
LoadingOrList(state = dataState.value) { list, value ->
viewModel.updateSelection(list, value)
}
EntryType(entryType = "All")
Card(modifier = Modifier.fillMaxWidth()) {
LoadingOrList(dataState.value) { list, value -> viewModel.updateSelection(list, value) }
}
Spacer(modifier = Modifier.height(6.dp))
EntryTypeBar(entryType = "All")
}
}
@Preview(showBackground = true)
@Composable
fun EntryTypeBar(entryType: String, modifier: Modifier = Modifier) {
val icon = Icons.Outlined.ArrowForward
val text = "Selected type: $entryType"
Card {
Row(verticalAlignment = Alignment.CenterVertically) {
// Icon(painter = painterResource(id = R.drawable.ic_next), contentDescription = null)
Icon(imageVector = icon, contentDescription = null, modifier = modifier.size(48.dp))
Text(text = text, maxLines = 1, modifier = modifier.fillMaxWidth())
private fun EntryTypeBarPreview() {
MainTheme { EntryType(entryType = "Amateur") }
}
@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
fun LoadingOrList(state: DataState<List<SatItem>>, onSelected: (List<Int>, Boolean) -> Unit) {
private fun LoadingOrList(
state: DataState<List<SatItem>>, onSelected: (List<Int>, Boolean) -> Unit
) {
when (state) {
is DataState.Loading -> {
LoadingProgress()
@ -77,7 +85,7 @@ fun LoadingOrList(state: DataState<List<SatItem>>, onSelected: (List<Int>, Boole
}
@Composable
fun LoadingProgress() {
private fun LoadingProgress() {
Row(
horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically
) { CircularProgressIndicator(modifier = Modifier.size(64.dp)) }
@ -85,26 +93,46 @@ fun LoadingProgress() {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun EntriesList(entries: List<SatItem>, onSelected: (List<Int>, Boolean) -> Unit) {
LazyVerticalGrid(columns = GridCells.Adaptive(200.dp)) {
private fun EntriesList(entries: List<SatItem>, onSelected: (List<Int>, Boolean) -> Unit) {
LazyColumn {
items(items = entries, key = { item -> item.catnum }) { entry ->
EntryItem(entry, onSelected, Modifier.animateItemPlacement())
Entry(entry, onSelected, Modifier.animateItemPlacement())
}
}
}
@Preview(showBackground = true)
@Composable
fun EntryItem(entry: SatItem, onSelected: (List<Int>, Boolean) -> Unit, modifier: Modifier) {
private fun EntryPreview() {
val satItem = SatItem(45555, "SatName", emptyList(), true)
MainTheme { Entry(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) }
}
@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)) {
Surface(modifier = Modifier.padding(bottom = 1.dp)) {
Row(verticalAlignment = Alignment.CenterVertically,
modifier = modifier
modifier = Modifier
.background(MaterialTheme.colorScheme.surface)
.onClick { onSelected(listOf(entry.catnum), entry.isSelected.not()) }) {
Checkbox(
checked = entry.isSelected, onCheckedChange = null, Modifier.padding(6.dp)
.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 = entry.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
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)
}
}
}
@ -112,10 +140,9 @@ fun EntryItem(entry: SatItem, onSelected: (List<Int>, Boolean) -> Unit, modifier
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchBar(setQuery: (String) -> Unit) {
private fun SearchBar(setQuery: (String) -> Unit) {
val currentQuery = rememberSaveable { mutableStateOf("") }
OutlinedTextField(
value = currentQuery.value,
OutlinedTextField(value = currentQuery.value,
onValueChange = { newValue ->
currentQuery.value = newValue
setQuery(newValue)
@ -123,9 +150,7 @@ fun SearchBar(setQuery: (String) -> Unit) {
maxLines = 1,
// label = { Text(text = stringResource(id = R.string.entries_search_hint)) },
placeholder = { Text(text = stringResource(id = R.string.entries_search_hint)) },
leadingIcon = {
Icon(imageVector = Icons.Outlined.Search, contentDescription = null)
},
leadingIcon = { Icon(imageVector = Icons.Outlined.Search, contentDescription = null) },
trailingIcon = {
Icon(imageVector = Icons.Outlined.Close,
contentDescription = null,
@ -138,8 +163,6 @@ fun SearchBar(setQuery: (String) -> Unit) {
// focusedBorderColor = MaterialTheme.colorScheme.onBackground,
// unfocusedBorderColor = MaterialTheme.colorScheme.onBackground
// ),
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surface)
modifier = Modifier.background(MaterialTheme.colorScheme.surface)
)
}

Wyświetl plik

@ -165,17 +165,17 @@
<string name="other_switch_sensors">Use sensors to rotate radar view</string>
<string name="outro_title">I would like to say thanks to:</string>
<string name="outro_thanks"> • You for downloading and using Look4Sat! \nMay your sky always be clear!
\n\n <a href="https://github.com/g4dpz">David A. B. Johnson</a>
<string name="outro_thanks">• You for downloading and using Look4Sat! \nMay your sky always be clear!
\n\n• <a href="https://github.com/g4dpz">David A. B. Johnson</a>
and <a href="https://github.com/davidmoten">Dave Moten</a> for creating predict4java lib
under the <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>.
\n\n <a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
\n\n• <a href="https://github.com/csete">Alexandru Csete</a> for creating Gpredict
satellite tracker that inspired the creation of Look4Sat.
\n\n <a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
\n\n• <a href="https://celestrak.com/webmaster.php">Dr T.S. Kelso</a> for maintaining his
<a href="https://celestrak.com/">Celestrak</a> website that provides access to the TLE data.
\n\n <a href="https://libre.space/">Libre Space Foundation</a> for their
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of satellite data.
\n\n <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
\n\n• <a href="https://libre.space/">Libre Space Foundation</a> for their
<a href="https://db.satnogs.org/">SatNOGS</a> API and DB providing a huge amount of data.
\n\n• <a href="https://appoftheday.downloadastro.com/app/look4sat-satellite-tracker/">DownloadAstro</a>
team for their interest to the app and the interview published.
</string>
<string name="outro_license">The app comes with no warranty.</string>