diff --git a/app/build.gradle b/app/build.gradle
index d769971a..cb124633 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,9 +6,8 @@ plugins {
}
android {
- compileSdkVersion 33
- buildToolsVersion '33.0.0'
namespace 'com.rtbishop.look4sat'
+ compileSdk 33
defaultConfig {
applicationId "com.rtbishop.look4sat"
@@ -21,7 +20,6 @@ android {
vectorDrawables {
useSupportLibrary true
}
-
kapt {
arguments {
arg("room.schemaLocation", "$projectDir/schemas")
@@ -31,6 +29,9 @@ android {
correctErrorTypes true
}
}
+ buildFeatures {
+ compose true
+ }
buildTypes {
release {
minifyEnabled true
@@ -38,29 +39,25 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
- buildFeatures {
- compose true
- viewBinding true
- }
- composeOptions {
- kotlinCompilerExtensionVersion compose_compiler_version
- }
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
+ composeOptions {
+ kotlinCompilerExtensionVersion compose_compiler_version
+ }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
- testOptions {
- animationsDisabled true
- unitTests.includeAndroidResources true
- }
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
+ testOptions {
+ animationsDisabled true
+ unitTests.includeAndroidResources true
+ }
}
kotlin {
@@ -69,43 +66,34 @@ kotlin {
dependencies {
implementation project(":base")
- implementation "androidx.core:core-ktx:$core_ktx_version"
- implementation "androidx.core:core-splashscreen:$core_splashscreen_version"
- implementation "androidx.constraintlayout:constraintlayout:$constraint_version"
- implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
- implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
- implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
- implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
+ // Android
+ implementation "androidx.core:core-splashscreen:$splashscreen_version"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
- implementation "com.google.dagger:hilt-android:$hilt_version"
- kapt "com.google.dagger:hilt-compiler:$hilt_version"
- implementation "com.jakewharton.timber:timber:$timber_version"
- implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
-
+ implementation "com.google.dagger:hilt-android:$hilt_android_version"
+ kapt "com.google.dagger:hilt-compiler:$hilt_android_version"
+ // Compose
implementation "androidx.activity:activity-compose:$activity_compose_version"
implementation "androidx.compose.animation:animation:$compose_version"
implementation "androidx.compose.compiler:compiler:$compose_compiler_version"
- implementation "androidx.compose.material:material:$material_version"
implementation "androidx.compose.material3:material3:$material3_version"
implementation "androidx.compose.runtime:runtime:$compose_version"
- implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
- implementation "androidx.compose.ui:ui-tooling:$compose_version"
+ implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.hilt:hilt-navigation-compose:$hilt_compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
implementation "androidx.navigation:navigation-compose:$navigation_version"
-
+ // Utility
+ implementation "com.jakewharton.timber:timber:$timber_version"
+ implementation "org.osmdroid:osmdroid-android:$osmdroid_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanary_version"
-
+ // Test
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
-
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
- androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_test_version"
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c95173dc..45364ace 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
-
+
@@ -12,13 +13,16 @@
+ android:theme="@style/Theme.Look4Sat.Main"
+ tools:targetApi="31">
-
\ No newline at end of file
+
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
deleted file mode 100644
index b31c292c..00000000
Binary files a/app/src/main/ic_launcher-playstore.png and /dev/null differ
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt
index 6834655a..6020c3ec 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/MainActivity.kt
@@ -22,8 +22,6 @@ import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.os.Bundle
-import android.os.SystemClock
-import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
@@ -47,15 +45,3 @@ class MainActivity : ComponentActivity() {
super.attachBaseContext(newBase)
}
}
-
-fun View.clickWithDebounce(debounceTime: Long = 875L, action: () -> Unit) {
- this.setOnClickListener(object : View.OnClickListener {
- private var lastClickTime: Long = 0
-
- override fun onClick(v: View) {
- if (SystemClock.elapsedRealtime() - lastClickTime < debounceTime) return
- else action()
- lastClickTime = SystemClock.elapsedRealtime()
- }
- })
-}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt
index b51e6aa0..4c63e4d7 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesScreen.kt
@@ -12,7 +12,7 @@ import androidx.compose.material.icons.outlined.PlayArrow
import androidx.compose.material.icons.outlined.Search
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -40,7 +40,7 @@ fun EntriesScreen(
viewModel: EntriesViewModel = hiltViewModel(),
passesViewModel: PassesViewModel = hiltViewModel()
) {
- val state = viewModel.satData.observeAsState(DataState.Loading)
+ val state = viewModel.satData.collectAsState(initial = DataState.Loading)
val showDialog = rememberSaveable { mutableStateOf(false) }
val toggleDialog = { showDialog.value = showDialog.value.not() }
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
index ab2538ea..92781e5f 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entriesScreen/EntriesViewModel.kt
@@ -17,50 +17,59 @@
*/
package com.rtbishop.look4sat.presentation.entriesScreen
-import androidx.lifecycle.*
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.model.SatItem
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.*
+import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
+@OptIn(ExperimentalCoroutinesApi::class)
@HiltViewModel
class EntriesViewModel @Inject constructor(
- private val repository: IDataRepository,
- private val settings: ISettingsManager
+ private val repository: IDataRepository, private val settings: ISettingsManager
) : ViewModel() {
- private val satType = MutableLiveData("All")
+ private val satType = MutableStateFlow("All")
// private val transModes = MutableLiveData(settings.loadModesSelection())
- private val currentQuery = MutableLiveData(String())
- private val itemsFromRepo = liveData {
- delay(250)
- emit(loadEntriesWithSelection())
- } as MutableLiveData
- private val itemsWithType = satType.switchMap { type ->
+ private val currentQuery = MutableStateFlow(String())
+ private val itemsFromRepo = MutableStateFlow>(emptyList())
+ private val itemsWithType = satType.flatMapLatest { type ->
itemsFromRepo.map { items -> filterByType(items, type) }
}
// private val itemsWithModes = transModes.switchMap { modes ->
// itemsWithType.map { items -> filterByModes(items, modes) }
// }
- private val itemsWithQuery = currentQuery.switchMap { query ->
+ private val itemsWithQuery = currentQuery.flatMapLatest { query ->
itemsWithType.map { items -> filterByQuery(items, query) }
}
val satData = itemsWithQuery.map { items -> DataState.Success(items) }
val satTypes: List = settings.sourcesMap.keys.sorted()
- fun getSatType() = satType.value ?: "All"
+ init {
+ viewModelScope.launch {
+ delay(250)
+ itemsFromRepo.value = loadEntriesWithSelection()
+ }
+ }
+
+ fun getSatType() = satType.value
fun setSatType(type: String) {
satType.value = type
}
fun selectCurrentItems(selectAll: Boolean) {
- itemsWithQuery.value?.let { itemsWithQuery ->
- updateSelection(itemsWithQuery.map { item -> item.catnum }, selectAll)
+ viewModelScope.launch {
+ val lastValue = itemsWithQuery.last()
+ updateSelection(lastValue.map { item -> item.catnum }, selectAll)
}
}
@@ -74,13 +83,13 @@ class EntriesViewModel @Inject constructor(
}
fun saveSelection(): List {
- return itemsFromRepo.value?.let { itemsAll ->
+ return itemsFromRepo.value.let { itemsAll ->
itemsAll.filter { item -> item.isSelected }.map { item -> item.catnum }
- } ?: emptyList()
+ }
}
fun updateSelection(catNums: List, isSelected: Boolean) {
- itemsFromRepo.value?.let { itemsAll ->
+ itemsFromRepo.value.let { itemsAll ->
val copiedList = itemsAll.map { item -> item.copy() }
catNums.forEach { catnum ->
copiedList.find { item -> item.catnum == catnum }?.isSelected = isSelected
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapScreen.kt
index 58d40993..ef8a5ff4 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapScreen.kt
@@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -63,10 +63,10 @@ fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
val context = LocalContext.current
Configuration.getInstance().load(context, viewModel.getPreferences())
viewModel.selectDefaultSatellite(-1)
- val stationPos = viewModel.stationPos.observeAsState()
- val positions = viewModel.positions.observeAsState()
- val satTrack = viewModel.track.observeAsState()
- val footprint = viewModel.footprint.observeAsState()
+ val stationPos = viewModel.stationPos.collectAsState(initial = null)
+ val positions = viewModel.positions.collectAsState(initial = null)
+ val satTrack = viewModel.track.collectAsState(initial = null)
+ val footprint = viewModel.footprint.collectAsState(initial = null)
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
Timber.d("MapScreen recomposition")
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
@@ -195,8 +195,7 @@ private fun MapView(modifier: Modifier = Modifier, update: ((map: MapView) -> Un
@Composable
private fun rememberMapViewWithLifecycle(context: Context = LocalContext.current): MapView {
- // add clipToOutline = true if needed
- val mapView = remember { MapView(context).apply { id = R.id.osm_map_view } }.apply {
+ val mapView = remember { MapView(context) }.apply {
setMultiTouchControls(true)
setTileSource(TileSourceFactory.WIKIMEDIA)
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
index d549de04..7070d237 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
@@ -18,7 +18,8 @@
package com.rtbishop.look4sat.presentation.mapScreen
import android.content.SharedPreferences
-import androidx.lifecycle.*
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager
import com.rtbishop.look4sat.domain.ISettingsManager
@@ -30,8 +31,12 @@ import com.rtbishop.look4sat.utility.toDegrees
import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.flow
import java.util.*
import javax.inject.Inject
+import kotlin.collections.set
import kotlin.math.max
import kotlin.math.min
@@ -50,23 +55,23 @@ class MapViewModel @Inject constructor(
private var dataUpdateRate = 1000L
private var selectedSatellite: Satellite? = null
- val stationPos = liveData {
+ val stationPos = flow {
val osmLat = clipLat(stationPosition.lat)
val osmLon = clipLon(stationPosition.lon)
emit(GeoPos(osmLat, osmLon))
}
- private val _track = MutableLiveData>>()
- val track: LiveData>> = _track
+ private val _track = MutableSharedFlow>>()
+ val track: SharedFlow>> = _track
- private val _footprint = MutableLiveData()
- val footprint: LiveData = _footprint
+ private val _footprint = MutableSharedFlow()
+ val footprint: SharedFlow = _footprint
- private val _mapData = MutableLiveData()
- val mapData: LiveData = this._mapData
+ private val _mapData = MutableSharedFlow()
+ val mapData: SharedFlow = _mapData
- private val _positions = MutableLiveData