Updated dependencies and gradle plugin, minor fixes

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:46 +00:00
1 parent 5040767296
commit d1b579ac4c
19 files changed
+370 -325

No files matched your search

+3 -5
View File
@@ -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
)
)
}
}
}
}
}
}
}
@@ -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()
}
@@ -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)
} }
} }
} }
+2 -10
View File
@@ -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) {
+5 -5
View File
@@ -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"