mirror of
https://github.com/rt-bishop/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Updated dependencies and gradle plugin, minor fixes
This commit is contained in:
1 parent
5040767296
commit
d1b579ac4c
19 files changed
+370
-325
No files matched your search
@@ -7,16 +7,15 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round">
|
||||||
android:theme="@style/Theme.Look4Sat.Main">
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/Theme.Look4Sat.SplashScreen">
|
android:theme="@style/Theme.Look4Sat.SplashScreen"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@@ -24,5 +23,4 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -20,21 +20,23 @@ package com.rtbishop.look4sat
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import com.rtbishop.look4sat.presentation.MainScreen
|
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
|
import com.rtbishop.look4sat.presentation.MainScreen
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
installSplashScreen()
|
installSplashScreen()
|
||||||
|
enableEdgeToEdge()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
val container = (LocalContext.current.applicationContext as MainApplication).container
|
val container = (LocalContext.current.applicationContext as MainApplication).container
|
||||||
val otherSettings = container.settingsRepo.otherSettings.collectAsState().value
|
val otherSettings = container.settingsRepo.otherSettings.collectAsState().value
|
||||||
MainTheme(isLightTheme = otherSettings.stateOfLightTheme) { MainScreen() }
|
MainTheme(isDarkTheme = !otherSettings.stateOfLightTheme) { MainScreen() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,19 +37,13 @@ class MainApplication : Application() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkAutoUpdate(timeNow: Long = System.currentTimeMillis()) {
|
private suspend fun checkAutoUpdate(timeNow: Long = System.currentTimeMillis()) {
|
||||||
val settingsRepo = container.settingsRepo
|
if (container.settingsRepo.otherSettings.value.stateOfAutoUpdate) {
|
||||||
if (settingsRepo.otherSettings.value.stateOfAutoUpdate) {
|
val timeDelta = timeNow - container.settingsRepo.databaseState.value.updateTimestamp
|
||||||
val timeDelta = timeNow - settingsRepo.databaseState.value.updateTimestamp
|
if (timeDelta > 172_800_000L) { // 48 hours in ms
|
||||||
if (timeDelta > AUTO_UPDATE_DELTA_MS) {
|
|
||||||
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault())
|
||||||
println("Started periodic data update on ${sdf.format(Date())}")
|
println("Started periodic data update on ${sdf.format(Date())}")
|
||||||
container.databaseRepo.updateFromRemote()
|
container.databaseRepo.updateFromRemote()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val AUTO_UPDATE_DELTA_MS = 172_800_000L // 48 hours in ms
|
|
||||||
const val MAX_OKHTTP_CACHE_SIZE = 10_000_000L // 10 Megabytes
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import okhttp3.Cache
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import org.osmdroid.config.Configuration
|
import org.osmdroid.config.Configuration
|
||||||
|
|
||||||
@@ -65,16 +64,13 @@ class MainContainer(private val context: Context) {
|
|||||||
|
|
||||||
private fun provideLocalSource(): ILocalSource {
|
private fun provideLocalSource(): ILocalSource {
|
||||||
val database = Room.databaseBuilder(context, Look4SatDb::class.java, "Look4SatDBv313").apply {
|
val database = Room.databaseBuilder(context, Look4SatDb::class.java, "Look4SatDBv313").apply {
|
||||||
// addMigrations(MIGRATION_1_2)
|
|
||||||
fallbackToDestructiveMigration()
|
fallbackToDestructiveMigration()
|
||||||
}.build()
|
}.build()
|
||||||
return LocalSource(database.look4SatDao())
|
return LocalSource(database.look4SatDao())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideRemoteSource(): IRemoteSource {
|
private fun provideRemoteSource(): IRemoteSource {
|
||||||
val cache = Cache(context.cacheDir, MainApplication.MAX_OKHTTP_CACHE_SIZE)
|
return RemoteSource(Dispatchers.IO, context.contentResolver, OkHttpClient.Builder().build())
|
||||||
val httpClient = OkHttpClient.Builder().cache(cache).build()
|
|
||||||
return RemoteSource(context.contentResolver, httpClient, Dispatchers.IO)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun provideSatelliteRepo(): ISatelliteRepo {
|
private fun provideSatelliteRepo(): ISatelliteRepo {
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
package com.rtbishop.look4sat.presentation
|
package com.rtbishop.look4sat.presentation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@@ -21,69 +20,89 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.rtbishop.look4sat.R
|
import com.rtbishop.look4sat.R
|
||||||
import com.rtbishop.look4sat.presentation.entries.EntriesScreen
|
|
||||||
import com.rtbishop.look4sat.presentation.entries.EntriesViewModel
|
|
||||||
import com.rtbishop.look4sat.presentation.info.InfoScreen
|
import com.rtbishop.look4sat.presentation.info.InfoScreen
|
||||||
import com.rtbishop.look4sat.presentation.map.MapScreen
|
import com.rtbishop.look4sat.presentation.map.MapScreen
|
||||||
import com.rtbishop.look4sat.presentation.passes.PassesScreen
|
import com.rtbishop.look4sat.presentation.passes.PassesScreen
|
||||||
import com.rtbishop.look4sat.presentation.passes.PassesViewModel
|
import com.rtbishop.look4sat.presentation.passes.PassesViewModel
|
||||||
import com.rtbishop.look4sat.presentation.radar.RadarScreen
|
import com.rtbishop.look4sat.presentation.radar.RadarScreen
|
||||||
|
import com.rtbishop.look4sat.presentation.satellites.SatellitesScreen
|
||||||
|
import com.rtbishop.look4sat.presentation.satellites.SatellitesViewModel
|
||||||
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
import com.rtbishop.look4sat.presentation.settings.SettingsScreen
|
||||||
|
|
||||||
sealed class Screen(var title: String, var icon: Int, var route: String) {
|
sealed class Screen(var title: String, var icon: Int, var route: String) {
|
||||||
data object Entries : Screen("Entries", R.drawable.ic_sputnik, "entries")
|
data object Main : Screen("Main", R.drawable.ic_sputnik, "main")
|
||||||
|
data object Radar : Screen("Radar", R.drawable.ic_sputnik, "radar")
|
||||||
|
data object Satellites : Screen("Satellites", R.drawable.ic_sputnik, "satellites")
|
||||||
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
|
data object Passes : Screen("Passes", R.drawable.ic_passes, "passes")
|
||||||
data object Radar : Screen("Radar", R.drawable.ic_passes, "passes.radar")
|
data object Map : Screen("Map", R.drawable.ic_map, "map")
|
||||||
data object Map : Screen("World Map", R.drawable.ic_world_map, "world_map")
|
|
||||||
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
|
data object Settings : Screen("Settings", R.drawable.ic_settings, "settings")
|
||||||
data object Info : Screen("Info", R.drawable.ic_info, "info")
|
data object Info : Screen("Info", R.drawable.ic_info, "info")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(navController: NavHostController = rememberNavController()) {
|
fun MainScreen() {
|
||||||
Scaffold(bottomBar = { MainNavBar(navController = navController) }) { innerPadding ->
|
val outerNavController: NavHostController = rememberNavController()
|
||||||
Box(modifier = Modifier.padding(innerPadding)) { MainNavGraph(navController) }
|
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
||||||
|
val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 },
|
||||||
|
navArgument("aosTime") { defaultValue = 0L })
|
||||||
|
val navToRadar = { catNum: Int, aosTime: Long ->
|
||||||
|
val navRoute = "${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}"
|
||||||
|
outerNavController.navigate(navRoute)
|
||||||
|
}
|
||||||
|
NavHost(navController = outerNavController, startDestination = Screen.Main.route) {
|
||||||
|
composable(Screen.Main.route) { NavBarScreen(navToRadar) }
|
||||||
|
composable(radarRoute, radarArgs) { RadarScreen() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun NavBarScreen(navToRadar: (Int, Long) -> Unit) {
|
||||||
|
val innerNavController: NavHostController = rememberNavController()
|
||||||
|
val navToPasses = { innerNavController.navigate(Screen.Passes.route) }
|
||||||
|
Scaffold(bottomBar = { MainNavBar(navController = innerNavController) }) { innerPadding ->
|
||||||
|
Box(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
NavHost(innerNavController, startDestination = Screen.Passes.route) {
|
||||||
|
composable(Screen.Satellites.route) {
|
||||||
|
val viewModel = viewModel(
|
||||||
|
SatellitesViewModel::class.java, factory = SatellitesViewModel.Factory
|
||||||
|
)
|
||||||
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
|
SatellitesScreen(uiState, navToPasses)
|
||||||
|
}
|
||||||
|
composable(Screen.Passes.route) {
|
||||||
|
val viewModel = viewModel(
|
||||||
|
PassesViewModel::class.java, factory = PassesViewModel.Factory
|
||||||
|
)
|
||||||
|
val uiState = viewModel.uiState.value
|
||||||
|
PassesScreen(uiState, navToRadar)
|
||||||
|
}
|
||||||
|
composable(Screen.Map.route) { MapScreen() }
|
||||||
|
composable(Screen.Settings.route) { SettingsScreen() }
|
||||||
|
composable(Screen.Info.route) { InfoScreen() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainNavBar(navController: NavController) {
|
private fun MainNavBar(navController: NavController) {
|
||||||
val items = listOf(Screen.Entries, Screen.Passes, Screen.Map, Screen.Settings, Screen.Info)
|
val items = listOf(Screen.Satellites, Screen.Passes, Screen.Map, Screen.Settings, Screen.Info)
|
||||||
NavigationBar(modifier = Modifier.height(48.dp), tonalElevation = 0.dp) {
|
val currentBackStackEntry = navController.currentBackStackEntryAsState()
|
||||||
val navBackStackEntry = navController.currentBackStackEntryAsState()
|
val currentRoute = currentBackStackEntry.value?.destination?.route
|
||||||
val currentRoute = navBackStackEntry.value?.destination?.route
|
NavigationBar {
|
||||||
items.forEach { screen ->
|
items.forEach { item ->
|
||||||
NavigationBarItem(selected = currentRoute?.contains(screen.route) ?: false, onClick = {
|
NavigationBarItem(selected = currentRoute?.contains(item.route) ?: false,
|
||||||
navController.navigate(screen.route) {
|
onClick = {
|
||||||
navController.graph.startDestinationRoute?.let { popUpTo(it) { saveState = false } }
|
navController.navigate(item.route) {
|
||||||
launchSingleTop = true
|
navController.graph.startDestinationRoute?.let {
|
||||||
restoreState = false
|
popUpTo(it) { saveState = false }
|
||||||
}
|
}
|
||||||
}, icon = { Icon(painterResource(id = screen.icon), contentDescription = screen.title) })
|
launchSingleTop = true
|
||||||
|
restoreState = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
|
||||||
|
label = { Text(item.title) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun MainNavGraph(navController: NavHostController) {
|
|
||||||
val radarRoute = "${Screen.Radar.route}?catNum={catNum}&aosTime={aosTime}"
|
|
||||||
val radarArgs = listOf(navArgument("catNum") { defaultValue = 0 }, navArgument("aosTime") { defaultValue = 0L })
|
|
||||||
NavHost(navController, startDestination = Screen.Passes.route) {
|
|
||||||
composable(Screen.Entries.route) {
|
|
||||||
val viewModel = viewModel(EntriesViewModel::class.java, factory = EntriesViewModel.Factory)
|
|
||||||
val navToPasses = { navController.navigate(Screen.Passes.route) }
|
|
||||||
EntriesScreen(viewModel.uiState.collectAsStateWithLifecycle().value, navToPasses)
|
|
||||||
}
|
|
||||||
composable(Screen.Passes.route) {
|
|
||||||
val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory)
|
|
||||||
val navToRadar = { catNum: Int, aosTime: Long ->
|
|
||||||
navController.navigate("${Screen.Radar.route}?catNum=${catNum}&aosTime=${aosTime}")
|
|
||||||
}
|
|
||||||
PassesScreen(viewModel.uiState.value, navToRadar)
|
|
||||||
}
|
|
||||||
composable(radarRoute, radarArgs) { RadarScreen() }
|
|
||||||
composable(Screen.Map.route) { MapScreen() }
|
|
||||||
composable(Screen.Settings.route) { SettingsScreen() }
|
|
||||||
composable(Screen.Info.route) { InfoScreen() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.rtbishop.look4sat.presentation
|
package com.rtbishop.look4sat.presentation
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Shapes
|
import androidx.compose.material3.Shapes
|
||||||
@@ -10,7 +11,6 @@ import androidx.compose.material3.lightColorScheme
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.toArgb
|
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
@@ -20,18 +20,16 @@ import androidx.compose.ui.unit.sp
|
|||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainTheme(isLightTheme: Boolean = false, content: @Composable () -> Unit) {
|
fun MainTheme(isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
|
||||||
val view = LocalView.current
|
val view = LocalView.current
|
||||||
if (view.isInEditMode) {
|
if (view.isInEditMode) {
|
||||||
MaterialTheme(darkScheme, shapes, typography, content)
|
MaterialTheme(darkScheme, shapes, typography, content)
|
||||||
} else {
|
} else {
|
||||||
val colorScheme = if (isLightTheme) lightScheme else darkScheme
|
val colorScheme = if (isDarkTheme) darkScheme else lightScheme
|
||||||
SideEffect {
|
SideEffect {
|
||||||
val window = (view.context as ComponentActivity).window
|
val window = (view.context as ComponentActivity).window
|
||||||
window.statusBarColor = colorScheme.background.toArgb()
|
|
||||||
window.navigationBarColor = colorScheme.surface.toArgb()
|
|
||||||
val insetsController = WindowCompat.getInsetsController(window, view)
|
val insetsController = WindowCompat.getInsetsController(window, view)
|
||||||
insetsController.isAppearanceLightStatusBars = isLightTheme
|
insetsController.isAppearanceLightStatusBars = false
|
||||||
}
|
}
|
||||||
MaterialTheme(colorScheme, shapes, typography, content)
|
MaterialTheme(colorScheme, shapes, typography, content)
|
||||||
}
|
}
|
||||||
@@ -57,22 +55,42 @@ private val lightScheme = lightColorScheme(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private val darkScheme = darkColorScheme(
|
private val darkScheme = darkColorScheme(
|
||||||
primary = Color(0xFFFFE082),
|
primary = Color(0xFFFFE082), // main accent, switch background, active progress
|
||||||
onPrimary = Color(0xFF000000),
|
onPrimary = Color(0xFF000000), // on main accent, switch knob
|
||||||
|
// primaryContainer = Color(0xFF121212),
|
||||||
|
// onPrimaryContainer = Color(0xFF121212),
|
||||||
|
// inversePrimary = Color(0xFF121212),
|
||||||
secondary = Color(0xFFE0E0E0),
|
secondary = Color(0xFFE0E0E0),
|
||||||
onSecondary = Color(0xFF000000),
|
onSecondary = Color(0xFF000000),
|
||||||
secondaryContainer = Color(0xFFFFE082), // navBar indicator
|
secondaryContainer = Color(0xFF484848), // navBar indicator,
|
||||||
onSecondaryContainer = Color(0xFF000000), // navBar active icon
|
onSecondaryContainer = Color(0xFFE0E0E0), // navBar active icon
|
||||||
|
// tertiary = Color(0xFF121212),
|
||||||
|
// onTertiary = Color(0xFF121212),
|
||||||
|
// tertiaryContainer = Color(0xFF121212),
|
||||||
|
// onTertiaryContainer = Color(0xFF121212),
|
||||||
background = Color(0xFF121212),
|
background = Color(0xFF121212),
|
||||||
onBackground = Color(0xFFE0E0E0),
|
onBackground = Color(0xFFE0E0E0),
|
||||||
surface = Color(0xFF242424),
|
surface = Color(0xFF242424), // card background
|
||||||
onSurface = Color(0xFFE0E0E0),
|
onSurface = Color(0xFFE0E0E0), // on card background
|
||||||
surfaceTint = Color(0x00000000),
|
|
||||||
surfaceVariant = Color(0xFF484848), // buttons background
|
surfaceVariant = Color(0xFF484848), // buttons background
|
||||||
onSurfaceVariant = Color(0xFFE0E0E0), // navBar inactive icon
|
onSurfaceVariant = Color(0xFFE0E0E0), // navBar inactive icon
|
||||||
|
surfaceTint = Color(0x00000000),
|
||||||
|
// inverseSurface = Color(0xFF121212),
|
||||||
|
// inverseOnSurface = Color(0xFF121212),
|
||||||
error = Color(0xFFDC0000),
|
error = Color(0xFFDC0000),
|
||||||
|
// onError = Color(0xFFFFFFFF),
|
||||||
|
// errorContainer = Color(0xFF121212),
|
||||||
|
// onErrorContainer = Color(0xFF121212),
|
||||||
outline = Color(0xA3E0E0E0),
|
outline = Color(0xA3E0E0E0),
|
||||||
scrim = Color(0xFF000000)
|
// outlineVariant = Color(0xFF121212),
|
||||||
|
scrim = Color(0xFF000000),
|
||||||
|
// surfaceBright = Color(0xFF121212),
|
||||||
|
surfaceContainer = Color(0xFF242424), // navBar background
|
||||||
|
// surfaceContainerHigh = Color(0xFF121212),
|
||||||
|
surfaceContainerHighest = Color(0xFF242424), // filled card background
|
||||||
|
surfaceContainerLow = Color(0xFF242424), // elevated card background
|
||||||
|
// surfaceContainerLowest = Color(0xFF121212),
|
||||||
|
// surfaceDim = Color(0xFF121212),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val shapes = Shapes(
|
private val shapes = Shapes(
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
package com.rtbishop.look4sat.presentation.entries
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
|
||||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
|
||||||
import androidx.compose.material3.RadioButton
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.rtbishop.look4sat.presentation.MainTheme
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
|
||||||
private fun TypeDialogPreview() {
|
|
||||||
val types = listOf("All", "Amateur", "Geostationary", "Military", "Weather")
|
|
||||||
MainTheme { TypesDialog(types, "All", {}) {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun TypesDialog(items: List<String>, selected: String, dismiss: () -> Unit, select: (String) -> Unit) {
|
|
||||||
ModalBottomSheet(
|
|
||||||
onDismissRequest = { dismiss() },
|
|
||||||
sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
|
||||||
modifier = Modifier.fillMaxHeight(0.80f)
|
|
||||||
) {
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Fixed(1),
|
|
||||||
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(1.dp)
|
|
||||||
) {
|
|
||||||
item { HorizontalDivider(thickness = 0.dp, color = MaterialTheme.colorScheme.surface) }
|
|
||||||
itemsIndexed(items) { index, item ->
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically,
|
|
||||||
modifier = Modifier
|
|
||||||
.background(MaterialTheme.colorScheme.surface)
|
|
||||||
.clickable { select(item) }) {
|
|
||||||
Text(
|
|
||||||
text = "$index).",
|
|
||||||
modifier = Modifier.padding(start = 12.dp, end = 6.dp),
|
|
||||||
fontWeight = FontWeight.Normal,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = item,
|
|
||||||
modifier = Modifier.weight(1f),
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
RadioButton(
|
|
||||||
selected = item == selected,
|
|
||||||
onClick = null,
|
|
||||||
modifier = Modifier.padding(start = 8.dp, top = 8.dp, end = 12.dp, bottom = 8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item { HorizontalDivider(thickness = 24.dp, color = MaterialTheme.colorScheme.surface) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package com.rtbishop.look4sat.presentation.entries
|
|
||||||
|
|
||||||
import com.rtbishop.look4sat.domain.model.SatItem
|
|
||||||
|
|
||||||
data class EntriesState(
|
|
||||||
val isDialogShown: Boolean,
|
|
||||||
val isLoading: Boolean,
|
|
||||||
val itemsList: List<SatItem>,
|
|
||||||
val currentType: String,
|
|
||||||
val typesList: List<String>,
|
|
||||||
val takeAction: (EntriesAction) -> Unit
|
|
||||||
)
|
|
||||||
|
|
||||||
sealed class EntriesAction {
|
|
||||||
data object SaveSelection : EntriesAction()
|
|
||||||
data class SearchFor(val query: String) : EntriesAction()
|
|
||||||
data object SelectAll : EntriesAction()
|
|
||||||
data class SelectSingle(val id: Int, val isTicked: Boolean) : EntriesAction()
|
|
||||||
data class SelectType(val type: String) : EntriesAction()
|
|
||||||
data object ToggleTypesDialog : EntriesAction()
|
|
||||||
data object UnselectAll : EntriesAction()
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.rtbishop.look4sat.presentation.passes
|
package com.rtbishop.look4sat.presentation.passes
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -27,7 +26,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -80,7 +78,6 @@ fun PassesScreen(uiState: PassesState, navToRadar: (Int, Long) -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PassesList(
|
private fun PassesList(
|
||||||
refreshState: PullRefreshState,
|
refreshState: PullRefreshState,
|
||||||
@@ -93,7 +90,7 @@ private fun PassesList(
|
|||||||
Box(modifier = Modifier.pullRefresh(refreshState), contentAlignment = Alignment.TopCenter) {
|
Box(modifier = Modifier.pullRefresh(refreshState), contentAlignment = Alignment.TopCenter) {
|
||||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||||
items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass ->
|
items(items = passes, key = { item -> item.catNum + item.aosTime }) { pass ->
|
||||||
PassItem(pass = pass, navToRadar = navToRadar, modifier = Modifier.animateItemPlacement())
|
PassItem(pass = pass, navToRadar = navToRadar, modifier = Modifier.animateItem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PullRefreshIndicator(refreshing = isRefreshing, state = refreshState, backgroundColor = backgroundColor)
|
PullRefreshIndicator(refreshing = isRefreshing, state = refreshState, backgroundColor = backgroundColor)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.items
|
|||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -34,27 +35,32 @@ fun RadarScreen() {
|
|||||||
val currentPass = viewModel.getPass().collectAsState(null).value
|
val currentPass = viewModel.getPass().collectAsState(null).value
|
||||||
val id = currentPass?.catNum ?: 99999
|
val id = currentPass?.catNum ?: 99999
|
||||||
val name = currentPass?.name ?: "Satellite"
|
val name = currentPass?.name ?: "Satellite"
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Scaffold { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(innerPadding),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||||
|
) {
|
||||||
// TimerBarNew(id, name, "88:88:88", R.drawable.ic_notifications) {}
|
// TimerBarNew(id, name, "88:88:88", R.drawable.ic_notifications) {}
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
) {
|
) {
|
||||||
viewModel.radarData.value?.let { data ->
|
viewModel.radarData.value?.let { data ->
|
||||||
RadarViewCompose(
|
RadarViewCompose(
|
||||||
item = data.orbitalPos,
|
item = data.orbitalPos,
|
||||||
items = data.satTrack,
|
items = data.satTrack,
|
||||||
azimElev = viewModel.orientation.value
|
azimElev = viewModel.orientation.value
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ElevatedCard(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.weight(1f)
|
||||||
|
) {
|
||||||
|
TransmittersList(transmitters = viewModel.transmitters.value)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ElevatedCard(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.weight(1f)
|
|
||||||
) {
|
|
||||||
TransmittersList(transmitters = viewModel.transmitters.value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +84,10 @@ private fun TransmitterItem(radio: SatRadio) {
|
|||||||
.background(MaterialTheme.colorScheme.surface)
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
.padding(6.dp)
|
.padding(6.dp)
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = R.drawable.ic_arrow),
|
painter = painterResource(id = R.drawable.ic_arrow),
|
||||||
contentDescription = null, modifier = Modifier.rotate(180f)
|
contentDescription = null, modifier = Modifier.rotate(180f)
|
||||||
@@ -93,7 +102,10 @@ private fun TransmitterItem(radio: SatRadio) {
|
|||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(id = R.string.radio_downlink, radio.downlinkLow ?: 0L),
|
text = stringResource(id = R.string.radio_downlink, radio.downlinkLow ?: 0L),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
|||||||
@@ -70,66 +70,66 @@ fun RadarViewCompose(item: OrbitalPos, items: List<OrbitalPos>, azimElev: Pair<F
|
|||||||
val beepSoundId = remember { mutableIntStateOf(0) }
|
val beepSoundId = remember { mutableIntStateOf(0) }
|
||||||
val aimTargetDifference = remember { mutableFloatStateOf(0f) }
|
val aimTargetDifference = remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
LaunchedEffect(item.azimuth, item.elevation, azimElev.first, azimElev.second) {
|
// LaunchedEffect(item.azimuth, item.elevation, azimElev.first, azimElev.second) {
|
||||||
val aimAzimuthRadians = azimElev.first.toDouble().toRadians()
|
// val aimAzimuthRadians = azimElev.first.toDouble().toRadians()
|
||||||
val aimElevationRadians = abs(min(azimElev.second, 0f)).toDouble().toRadians()
|
// val aimElevationRadians = abs(min(azimElev.second, 0f)).toDouble().toRadians()
|
||||||
// radius of 0.5 makes the aimTargetDifference range 0.0 to 1.0
|
// // radius of 0.5 makes the aimTargetDifference range 0.0 to 1.0
|
||||||
val radius = 0.5
|
// val radius = 0.5
|
||||||
val aimX = sph2CartX(aimAzimuthRadians, aimElevationRadians, radius)
|
// val aimX = sph2CartX(aimAzimuthRadians, aimElevationRadians, radius)
|
||||||
val aimY = sph2CartY(aimAzimuthRadians, aimElevationRadians, radius)
|
// val aimY = sph2CartY(aimAzimuthRadians, aimElevationRadians, radius)
|
||||||
val satX = sph2CartX(item.azimuth, item.elevation, radius)
|
// val satX = sph2CartX(item.azimuth, item.elevation, radius)
|
||||||
val satY = sph2CartY(item.azimuth, item.elevation, radius)
|
// val satY = sph2CartY(item.azimuth, item.elevation, radius)
|
||||||
aimTargetDifference.floatValue = sqrt((satX - aimX).pow(2) + (satY - aimY).pow(2))
|
// aimTargetDifference.floatValue = sqrt((satX - aimX).pow(2) + (satY - aimY).pow(2))
|
||||||
|
//
|
||||||
|
//
|
||||||
val minPlaybackRate = 0.5f
|
// val minPlaybackRate = 0.5f
|
||||||
val maxPlaybackRate = 2.0f
|
// val maxPlaybackRate = 2.0f
|
||||||
val playbackRate =
|
// val playbackRate =
|
||||||
maxPlaybackRate - (aimTargetDifference.floatValue / (maxPlaybackRate - minPlaybackRate))
|
// maxPlaybackRate - (aimTargetDifference.floatValue / (maxPlaybackRate - minPlaybackRate))
|
||||||
|
//
|
||||||
soundPool.value?.setRate(beepSoundId.intValue, playbackRate)
|
// soundPool.value?.setRate(beepSoundId.intValue, playbackRate)
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
LaunchedEffect(aimTargetDifference.floatValue < aimThreshold) {
|
// LaunchedEffect(aimTargetDifference.floatValue < aimThreshold) {
|
||||||
if (aimTargetDifference.floatValue < aimThreshold) {
|
// if (aimTargetDifference.floatValue < aimThreshold) {
|
||||||
view.performHapticFeedback(HapticFeedbackConstantsCompat.VIRTUAL_KEY)
|
// view.performHapticFeedback(HapticFeedbackConstantsCompat.VIRTUAL_KEY)
|
||||||
soundPool.value?.pause(beepSoundId.intValue)
|
// soundPool.value?.pause(beepSoundId.intValue)
|
||||||
} else {
|
// } else {
|
||||||
soundPool.value?.resume(beepSoundId.intValue)
|
// soundPool.value?.resume(beepSoundId.intValue)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
LaunchedEffect(item.elevation > 0) {
|
// LaunchedEffect(item.elevation > 0) {
|
||||||
if(item.elevation > 0) {
|
// if(item.elevation > 0) {
|
||||||
soundPool.value?.resume(beepSoundId.intValue)
|
// soundPool.value?.resume(beepSoundId.intValue)
|
||||||
} else {
|
// } else {
|
||||||
soundPool.value?.pause(beepSoundId.intValue)
|
// soundPool.value?.pause(beepSoundId.intValue)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
DisposableEffect(Unit) {
|
// DisposableEffect(Unit) {
|
||||||
val audioAttributes = AudioAttributes.Builder()
|
// val audioAttributes = AudioAttributes.Builder()
|
||||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
// .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
||||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
// .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||||
.build()
|
// .build()
|
||||||
|
//
|
||||||
soundPool.value = SoundPool.Builder()
|
// soundPool.value = SoundPool.Builder()
|
||||||
.setMaxStreams(1)
|
// .setMaxStreams(1)
|
||||||
.setAudioAttributes(audioAttributes)
|
// .setAudioAttributes(audioAttributes)
|
||||||
.build()
|
// .build()
|
||||||
|
//
|
||||||
beepSoundId.intValue = soundPool.value?.load(context, R.raw.beep, 1) ?: 0
|
// beepSoundId.intValue = soundPool.value?.load(context, R.raw.beep, 1) ?: 0
|
||||||
|
//
|
||||||
soundPool.value?.setOnLoadCompleteListener { soundPool, _, status ->
|
// soundPool.value?.setOnLoadCompleteListener { soundPool, _, status ->
|
||||||
if (status == 0) {
|
// if (status == 0) {
|
||||||
soundPool.play(beepSoundId.intValue, 0.5f, 0.5f, 0, -1, 1f)
|
// soundPool.play(beepSoundId.intValue, 0.5f, 0.5f, 0, -1, 1f)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
onDispose {
|
// onDispose {
|
||||||
soundPool.value?.release()
|
// soundPool.value?.release()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
val radius = (size.minDimension / 2f) * 0.95f
|
val radius = (size.minDimension / 2f) * 0.95f
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package com.rtbishop.look4sat.presentation.satellites
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||||
|
import androidx.compose.material3.ElevatedCard
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
|
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.LocalConfiguration
|
||||||
|
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.compose.ui.window.Dialog
|
||||||
|
import com.rtbishop.look4sat.presentation.MainTheme
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun TypeDialogPreview() {
|
||||||
|
val types = listOf("All", "Amateur", "Geostationary", "Military", "Weather")
|
||||||
|
MainTheme { TypesDialog(types, "All", {}) {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TypesDialog(
|
||||||
|
items: List<String>, selected: String, dismiss: () -> Unit, select: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val height = LocalConfiguration.current.screenHeightDp
|
||||||
|
Dialog(onDismissRequest = { dismiss() }) {
|
||||||
|
ElevatedCard {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.heightIn(max = height.times(0.80).dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Select category",
|
||||||
|
fontSize = 18.sp,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp)
|
||||||
|
)
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Adaptive(240.dp),
|
||||||
|
modifier = Modifier.background(MaterialTheme.colorScheme.background),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(1.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||||
|
) {
|
||||||
|
itemsIndexed(items) { index, item ->
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.surface)
|
||||||
|
.clickable { select(item) }) {
|
||||||
|
Text(
|
||||||
|
text = "$index).",
|
||||||
|
modifier = Modifier.padding(start = 12.dp, end = 6.dp),
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = item,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
RadioButton(
|
||||||
|
selected = item == selected,
|
||||||
|
onClick = null,
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
start = 8.dp, top = 8.dp, end = 12.dp, bottom = 8.dp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
-29
@@ -1,6 +1,5 @@
|
|||||||
package com.rtbishop.look4sat.presentation.entries
|
package com.rtbishop.look4sat.presentation.satellites
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -44,29 +43,29 @@ import com.rtbishop.look4sat.presentation.components.CardIcon
|
|||||||
import com.rtbishop.look4sat.presentation.components.CardLoadingIndicator
|
import com.rtbishop.look4sat.presentation.components.CardLoadingIndicator
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EntriesScreen(uiState: EntriesState, navToPasses: () -> Unit) {
|
fun SatellitesScreen(uiState: SatellitesState, navToPasses: () -> Unit) {
|
||||||
val toggleDialog = { uiState.takeAction(EntriesAction.ToggleTypesDialog) }
|
val toggleDialog = { uiState.takeAction(SatellitesAction.ToggleTypesDialog) }
|
||||||
if (uiState.isDialogShown) {
|
if (uiState.isDialogShown) {
|
||||||
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
|
TypesDialog(items = uiState.typesList, selected = uiState.currentType, toggleDialog) {
|
||||||
uiState.takeAction(EntriesAction.SelectType(it))
|
uiState.takeAction(SatellitesAction.SelectType(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val unselectAll = { uiState.takeAction(EntriesAction.UnselectAll) }
|
val unselectAll = { uiState.takeAction(SatellitesAction.UnselectAll) }
|
||||||
val selectAll = { uiState.takeAction(EntriesAction.SelectAll) }
|
val selectAll = { uiState.takeAction(SatellitesAction.SelectAll) }
|
||||||
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||||
TopBar(
|
TopBar(setQuery = { newQuery: String ->
|
||||||
setQuery = { newQuery: String -> uiState.takeAction(EntriesAction.SearchFor(newQuery)) },
|
uiState.takeAction(SatellitesAction.SearchFor(newQuery))
|
||||||
saveSelection = {
|
}, saveSelection = {
|
||||||
uiState.takeAction(EntriesAction.SaveSelection)
|
uiState.takeAction(SatellitesAction.SaveSelection)
|
||||||
navToPasses()
|
navToPasses()
|
||||||
})
|
})
|
||||||
MiddleBar(uiState.currentType, { toggleDialog() }, { unselectAll() }, { selectAll() })
|
MiddleBar(uiState.currentType, { toggleDialog() }, { unselectAll() }, { selectAll() })
|
||||||
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
ElevatedCard(modifier = Modifier.fillMaxSize()) {
|
||||||
if (uiState.isLoading) {
|
if (uiState.isLoading) {
|
||||||
CardLoadingIndicator()
|
CardLoadingIndicator()
|
||||||
} else {
|
} else {
|
||||||
EntriesCard(uiState.itemsList) { id, isTicked ->
|
SatellitesCard(uiState.itemsList) { id, isTicked ->
|
||||||
uiState.takeAction(EntriesAction.SelectSingle(id, isTicked))
|
uiState.takeAction(SatellitesAction.SelectSingle(id, isTicked))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +104,9 @@ private fun SearchBar(setQuery: (String) -> Unit, modifier: Modifier = Modifier)
|
|||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = modifier.padding(start = 12.dp),
|
modifier = modifier.padding(start = 12.dp),
|
||||||
textStyle = TextStyle(
|
textStyle = TextStyle(
|
||||||
fontSize = 16.sp, fontWeight = FontWeight.Medium, color = MaterialTheme.colorScheme.onSurface
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
),
|
),
|
||||||
decorationBox = { innerTextField ->
|
decorationBox = { innerTextField ->
|
||||||
if (currentQuery.value.isEmpty()) {
|
if (currentQuery.value.isEmpty()) {
|
||||||
@@ -147,14 +148,14 @@ private fun MiddleBarPreview() = MainTheme { MiddleBar("Amateur", {}, {}, {}) }
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MiddleBar(type: String, navigate: () -> Unit, uncheck: () -> Unit, check: () -> Unit) {
|
private fun MiddleBar(type: String, navigate: () -> Unit, uncheck: () -> Unit, check: () -> Unit) {
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.height(48.dp)) {
|
||||||
EntryTypeCard(type = type, { navigate() }, modifier = Modifier.weight(1f))
|
TypeCard(type = type, { navigate() }, modifier = Modifier.weight(1f))
|
||||||
CardIcon(onClick = { uncheck() }, iconId = R.drawable.ic_check_off)
|
CardIcon(onClick = { uncheck() }, iconId = R.drawable.ic_check_off)
|
||||||
CardIcon(onClick = { check() }, iconId = R.drawable.ic_check_on)
|
CardIcon(onClick = { check() }, iconId = R.drawable.ic_check_on)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EntryTypeCard(type: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
private fun TypeCard(type: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
ElevatedCard(modifier = modifier) {
|
ElevatedCard(modifier = modifier) {
|
||||||
Row(horizontalArrangement = Arrangement.Start,
|
Row(horizontalArrangement = Arrangement.Start,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -180,13 +181,13 @@ private fun EntryTypeCard(type: String, onClick: () -> Unit, modifier: Modifier
|
|||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun EntryPreview() {
|
private fun SatellitePreview() {
|
||||||
val satItem = SatItem(44444, "Ultra Super Mega long satellite name", true)
|
val satItem = SatItem(44444, "Ultra Super Mega long satellite name", true)
|
||||||
MainTheme { Entry(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) }
|
MainTheme { Satellite(item = satItem, onSelected = { _, _ -> run {} }, modifier = Modifier) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Entry(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) {
|
private fun Satellite(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: Modifier) {
|
||||||
val passSatId = stringResource(id = R.string.pass_satId, item.catnum)
|
val passSatId = stringResource(id = R.string.pass_satId, item.catnum)
|
||||||
Surface(color = MaterialTheme.colorScheme.background,
|
Surface(color = MaterialTheme.colorScheme.background,
|
||||||
modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) {
|
modifier = modifier.clickable { onSelected(item.catnum, item.isSelected) }) {
|
||||||
@@ -198,8 +199,7 @@ private fun Entry(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: M
|
|||||||
.padding(start = 14.dp, top = 8.dp, end = 12.dp, bottom = 8.dp)
|
.padding(start = 14.dp, top = 8.dp, end = 12.dp, bottom = 8.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "$passSatId - ",
|
text = "$passSatId - ", color = MaterialTheme.colorScheme.primary
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = item.name,
|
text = item.name,
|
||||||
@@ -208,7 +208,11 @@ private fun Entry(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: M
|
|||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
Checkbox(checked = item.isSelected, onCheckedChange = null, modifier = Modifier.padding(start = 6.dp))
|
Checkbox(
|
||||||
|
checked = item.isSelected,
|
||||||
|
onCheckedChange = null,
|
||||||
|
modifier = Modifier.padding(start = 6.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,21 +220,20 @@ private fun Entry(item: SatItem, onSelected: (Int, Boolean) -> Unit, modifier: M
|
|||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
private fun EntriesPreview() {
|
private fun SatellitesPreview() {
|
||||||
val entries = listOf(
|
val entries = listOf(
|
||||||
SatItem(8888, "Meteor", false),
|
SatItem(8888, "Meteor", false),
|
||||||
SatItem(44444, "ISS", true),
|
SatItem(44444, "ISS", true),
|
||||||
SatItem(88888, "Starlink", false)
|
SatItem(88888, "Starlink", false)
|
||||||
)
|
)
|
||||||
MainTheme { EntriesCard(entries) { _, _ -> run {} } }
|
MainTheme { SatellitesCard(entries) { _, _ -> run {} } }
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EntriesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
fun SatellitesCard(items: List<SatItem>, onSelected: (Int, Boolean) -> Unit) {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
items(items = items, key = { item -> item.catnum }) { entry ->
|
items(items = items, key = { item -> item.catnum }) { entry ->
|
||||||
Entry(entry, onSelected, Modifier.animateItemPlacement())
|
Satellite(entry, onSelected, Modifier.animateItem())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.rtbishop.look4sat.presentation.satellites
|
||||||
|
|
||||||
|
import com.rtbishop.look4sat.domain.model.SatItem
|
||||||
|
|
||||||
|
data class SatellitesState(
|
||||||
|
val isDialogShown: Boolean,
|
||||||
|
val isLoading: Boolean,
|
||||||
|
val itemsList: List<SatItem>,
|
||||||
|
val currentType: String,
|
||||||
|
val typesList: List<String>,
|
||||||
|
val takeAction: (SatellitesAction) -> Unit
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed class SatellitesAction {
|
||||||
|
data object SaveSelection : SatellitesAction()
|
||||||
|
data class SearchFor(val query: String) : SatellitesAction()
|
||||||
|
data object SelectAll : SatellitesAction()
|
||||||
|
data class SelectSingle(val id: Int, val isTicked: Boolean) : SatellitesAction()
|
||||||
|
data class SelectType(val type: String) : SatellitesAction()
|
||||||
|
data object ToggleTypesDialog : SatellitesAction()
|
||||||
|
data object UnselectAll : SatellitesAction()
|
||||||
|
}
|
||||||
+13
-13
@@ -15,7 +15,7 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package com.rtbishop.look4sat.presentation.entries
|
package com.rtbishop.look4sat.presentation.satellites
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
@@ -29,11 +29,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
|
class SatellitesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel() {
|
||||||
|
|
||||||
private val defaultType = "All"
|
private val defaultType = "All"
|
||||||
private val _uiState = MutableStateFlow(
|
private val _uiState = MutableStateFlow(
|
||||||
EntriesState(
|
SatellitesState(
|
||||||
isDialogShown = false,
|
isDialogShown = false,
|
||||||
isLoading = true,
|
isLoading = true,
|
||||||
itemsList = emptyList(),
|
itemsList = emptyList(),
|
||||||
@@ -42,7 +42,7 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
|
|||||||
takeAction = ::handleAction
|
takeAction = ::handleAction
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val uiState: StateFlow<EntriesState> = _uiState
|
val uiState: StateFlow<SatellitesState> = _uiState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -54,15 +54,15 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleAction(action: EntriesAction) {
|
private fun handleAction(action: SatellitesAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
EntriesAction.SaveSelection -> saveSelection()
|
SatellitesAction.SaveSelection -> saveSelection()
|
||||||
is EntriesAction.SearchFor -> searchFor(action.query)
|
is SatellitesAction.SearchFor -> searchFor(action.query)
|
||||||
EntriesAction.SelectAll -> selectAll(true)
|
SatellitesAction.SelectAll -> selectAll(true)
|
||||||
is EntriesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
|
is SatellitesAction.SelectSingle -> selectSingle(action.id, action.isTicked)
|
||||||
is EntriesAction.SelectType -> selectType(action.type)
|
is SatellitesAction.SelectType -> selectType(action.type)
|
||||||
EntriesAction.ToggleTypesDialog -> toggleTypesDialog()
|
SatellitesAction.ToggleTypesDialog -> toggleTypesDialog()
|
||||||
EntriesAction.UnselectAll -> selectAll(false)
|
SatellitesAction.UnselectAll -> selectAll(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ class EntriesViewModel(private val selectionRepo: ISelectionRepo) : ViewModel()
|
|||||||
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY
|
||||||
initializer {
|
initializer {
|
||||||
val container = (this[applicationKey] as MainApplication).container
|
val container = (this[applicationKey] as MainApplication).container
|
||||||
EntriesViewModel(container.selectionRepo)
|
SatellitesViewModel(container.selectionRepo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File renamed without changes.
@@ -1,20 +1,12 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
|
|
||||||
<color name="accent">#FFE082</color>
|
<color name="accent">#FFE082</color>
|
||||||
<color name="background">#121212</color>
|
<color name="background">#121212</color>
|
||||||
|
|
||||||
<style name="Theme.Look4Sat.SplashScreen" parent="Theme.SplashScreen">
|
<style name="Theme.Look4Sat.SplashScreen" parent="Theme.SplashScreen">
|
||||||
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
<item name="postSplashScreenTheme">@android:style/Theme.Material.NoActionBar</item>
|
||||||
<item name="postSplashScreenTheme">@style/Theme.Look4Sat.Main</item>
|
|
||||||
<item name="windowSplashScreenBackground">@color/background</item>
|
<item name="windowSplashScreenBackground">@color/background</item>
|
||||||
<item name="windowSplashScreenAnimatedIcon">@drawable/launcher_fg</item>
|
<item name="windowSplashScreenAnimatedIcon">@drawable/launcher_fg</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Look4Sat.Main" parent="android:Theme.Material.Light.NoActionBar">
|
|
||||||
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
|
|
||||||
<item name="android:navigationBarColor">@color/background</item>
|
|
||||||
<item name="android:statusBarColor">@color/background</item>
|
|
||||||
<item name="android:windowBackground">@color/background</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -27,9 +27,9 @@ import okhttp3.OkHttpClient
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
|
|
||||||
class RemoteSource(
|
class RemoteSource(
|
||||||
|
private val dispatcher: CoroutineDispatcher,
|
||||||
private val contentResolver: ContentResolver,
|
private val contentResolver: ContentResolver,
|
||||||
private val httpClient: OkHttpClient,
|
private val httpClient: OkHttpClient
|
||||||
private val dispatcher: CoroutineDispatcher
|
|
||||||
) : IRemoteSource {
|
) : IRemoteSource {
|
||||||
|
|
||||||
override suspend fun getFileStream(uri: String): InputStream? = withContext(dispatcher) {
|
override suspend fun getFileStream(uri: String): InputStream? = withContext(dispatcher) {
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
[versions]
|
[versions]
|
||||||
android-gradle-plugin = "8.7.1"
|
android-gradle-plugin = "8.7.2"
|
||||||
google-ksp = "2.0.20-1.0.25"
|
google-ksp = "2.0.20-1.0.25"
|
||||||
kotlin = "2.0.21"
|
kotlin = "2.0.21"
|
||||||
|
|
||||||
androidx-core-ktx = "1.13.1"
|
androidx-core-ktx = "1.15.0"
|
||||||
androidx-core-splashscreen = "1.0.1"
|
androidx-core-splashscreen = "1.0.1"
|
||||||
androidx-room = "2.6.1"
|
androidx-room = "2.6.1"
|
||||||
|
|
||||||
composeBom = "2024.10.00"
|
composeBom = "2024.10.01"
|
||||||
compose-activity = "1.9.3"
|
compose-activity = "1.9.3"
|
||||||
compose-lifecycle = "2.8.6"
|
compose-lifecycle = "2.8.7"
|
||||||
compose-material3 = "1.3.0"
|
compose-material3 = "1.3.1"
|
||||||
compose-navigation = "2.8.3"
|
compose-navigation = "2.8.3"
|
||||||
|
|
||||||
other-coroutines = "1.9.0"
|
other-coroutines = "1.9.0"
|
||||||
|
|||||||
Reference in new issue
Block a user