diff --git a/app/build.gradle b/app/build.gradle index cb124633..525554ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,11 @@ plugins { id "com.android.application" + id "com.google.devtools.ksp" id "org.jetbrains.kotlin.android" - id "org.jetbrains.kotlin.kapt" - id "dagger.hilt.android.plugin" } android { - namespace 'com.rtbishop.look4sat' + namespace "com.rtbishop.look4sat" compileSdk 33 defaultConfig { @@ -14,20 +13,9 @@ android { minSdk 21 targetSdk 33 versionCode 312 - versionName '3.1.2' + versionName "3.1.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { - useSupportLibrary true - } - kapt { - arguments { - arg("room.schemaLocation", "$projectDir/schemas") - arg("room.incremental", "true") - arg("room.expandProjection", "true") - } - correctErrorTypes true - } } buildFeatures { compose true @@ -40,14 +28,14 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } composeOptions { kotlinCompilerExtensionVersion compose_compiler_version } kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() } packagingOptions { resources { @@ -61,7 +49,7 @@ android { } kotlin { - jvmToolchain(11) + jvmToolchain(17) } dependencies { @@ -70,9 +58,7 @@ dependencies { 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_android_version" - kapt "com.google.dagger:hilt-compiler:$hilt_android_version" + ksp "androidx.room:room-compiler:$room_version" // Compose implementation "androidx.activity:activity-compose:$activity_compose_version" implementation "androidx.compose.animation:animation:$compose_version" @@ -81,7 +67,6 @@ dependencies { implementation "androidx.compose.runtime:runtime:$compose_version" implementation "androidx.compose.ui:ui:$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 diff --git a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt index c543c343..ef057571 100644 --- a/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt +++ b/app/src/main/java/com/rtbishop/look4sat/MainActivity.kt @@ -25,9 +25,7 @@ import androidx.activity.compose.setContent import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import com.rtbishop.look4sat.presentation.MainScreen import com.rtbishop.look4sat.presentation.MainTheme -import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/rtbishop/look4sat/MainApplication.kt b/app/src/main/java/com/rtbishop/look4sat/MainApplication.kt index e6fa2a15..80cc7815 100644 --- a/app/src/main/java/com/rtbishop/look4sat/MainApplication.kt +++ b/app/src/main/java/com/rtbishop/look4sat/MainApplication.kt @@ -18,25 +18,18 @@ package com.rtbishop.look4sat import android.app.Application -import com.rtbishop.look4sat.domain.IDataRepository -import com.rtbishop.look4sat.domain.ISettingsRepository -import dagger.hilt.android.HiltAndroidApp import timber.log.Timber import java.text.SimpleDateFormat -import java.util.* -import javax.inject.Inject +import java.util.Date +import java.util.Locale -@HiltAndroidApp class MainApplication : Application() { - @Inject - lateinit var dataRepository: IDataRepository - - @Inject - lateinit var settingsRepository: ISettingsRepository + lateinit var container: MainContainer override fun onCreate() { super.onCreate() + container = MainContainer(this) if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) } @@ -44,12 +37,13 @@ class MainApplication : Application() { } private fun checkAutoUpdate() { + val settingsRepository = container.settingsRepository if (settingsRepository.isUpdateEnabled()) { val timeDelta = System.currentTimeMillis() - settingsRepository.getLastUpdateTime() if (timeDelta > 172800000) { // 48 hours in ms val sdf = SimpleDateFormat("d MMM yyyy - HH:mm:ss", Locale.getDefault()) Timber.d("Started periodic data update on ${sdf.format(Date())}") - dataRepository.updateFromWeb() + container.dataRepository.updateFromWeb() } } } diff --git a/app/src/main/java/com/rtbishop/look4sat/MainModule.kt b/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt similarity index 53% rename from app/src/main/java/com/rtbishop/look4sat/MainModule.kt rename to app/src/main/java/com/rtbishop/look4sat/MainContainer.kt index 5d9729be..74019ffe 100644 --- a/app/src/main/java/com/rtbishop/look4sat/MainModule.kt +++ b/app/src/main/java/com/rtbishop/look4sat/MainContainer.kt @@ -1,20 +1,3 @@ -/* - * Look4Sat. Amateur radio satellite tracker and pass predictor. - * Copyright (C) 2019-2022 Arty Bishop (bishop.arty@gmail.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ package com.rtbishop.look4sat import android.bluetooth.BluetoothManager @@ -26,45 +9,43 @@ import androidx.room.Room import com.rtbishop.look4sat.data.DataParser import com.rtbishop.look4sat.data.DataRepository import com.rtbishop.look4sat.data.SatelliteRepository -import com.rtbishop.look4sat.domain.* -import com.rtbishop.look4sat.framework.* -import com.rtbishop.look4sat.framework.data.* +import com.rtbishop.look4sat.domain.IDataRepository +import com.rtbishop.look4sat.domain.ISatelliteRepository +import com.rtbishop.look4sat.domain.ISensorRepository +import com.rtbishop.look4sat.domain.ISettingsRepository +import com.rtbishop.look4sat.framework.BluetoothReporter +import com.rtbishop.look4sat.framework.NetworkReporter +import com.rtbishop.look4sat.framework.SensorRepository +import com.rtbishop.look4sat.framework.SettingsRepository +import com.rtbishop.look4sat.framework.data.MIGRATION_1_2 +import com.rtbishop.look4sat.framework.data.MainDatabase import com.rtbishop.look4sat.framework.data.local.EntriesStorage import com.rtbishop.look4sat.framework.data.local.RadiosStorage import com.rtbishop.look4sat.framework.data.remote.FileSource import com.rtbishop.look4sat.framework.data.remote.NetworkSource -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob -import javax.inject.Singleton -@Module -@InstallIn(SingletonComponent::class) -object MainModule { +class MainContainer(private val context: Context) { - @Provides - @Singleton - fun provideBluetoothReporter(@ApplicationContext context: Context): BluetoothReporter { + val bluetoothReporter = provideBluetoothReporter() + val networkReporter = provideNetworkReporter() + val settingsRepository = provideSettingsRepository() + val dataRepository = provideDataRepository(settingsRepository) + val sensorRepository = provideSensorRepository() + val satelliteRepository = provideSatelliteRepository() + + private fun provideBluetoothReporter(): BluetoothReporter { val manager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager return BluetoothReporter(manager, CoroutineScope(Dispatchers.IO)) } - @Provides - @Singleton - fun provideNetworkReporter(): NetworkReporter { + private fun provideNetworkReporter(): NetworkReporter { return NetworkReporter(CoroutineScope(Dispatchers.IO)) } - @Provides - @Singleton - fun provideDataRepository( - @ApplicationContext context: Context, settings: ISettingsRepository - ): IDataRepository { + private fun provideDataRepository(settings: ISettingsRepository): IDataRepository { val db = Room.databaseBuilder(context, MainDatabase::class.java, "Look4SatDb") .addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build() val parser = DataParser(Dispatchers.Default) @@ -76,23 +57,17 @@ object MainModule { return DataRepository(parser, fileSource, entries, radios, remoteSource, scope, settings) } - @Provides - @Singleton - fun provideSensorRepository(@ApplicationContext context: Context): ISensorRepository { + private fun provideSensorRepository(): ISensorRepository { val manager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager val sensor = manager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) return SensorRepository(manager, sensor) } - @Provides - @Singleton - fun provideSatelliteRepository(): ISatelliteRepository { + private fun provideSatelliteRepository(): ISatelliteRepository { return SatelliteRepository(Dispatchers.Default) } - @Provides - @Singleton - fun provideSettingsRepository(@ApplicationContext context: Context): ISettingsRepository { + private fun provideSettingsRepository(): ISettingsRepository { val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE) return SettingsRepository(manager, preferences) diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/data/MainDatabase.kt b/app/src/main/java/com/rtbishop/look4sat/framework/data/MainDatabase.kt index ab40b90d..576438ca 100644 --- a/app/src/main/java/com/rtbishop/look4sat/framework/data/MainDatabase.kt +++ b/app/src/main/java/com/rtbishop/look4sat/framework/data/MainDatabase.kt @@ -26,7 +26,7 @@ import com.rtbishop.look4sat.framework.data.dao.RadiosDao import com.rtbishop.look4sat.framework.model.SatEntry import com.rtbishop.look4sat.framework.model.SatRadio -@Database(entities = [SatEntry::class, SatRadio::class], version = 2, exportSchema = true) +@Database(entities = [SatEntry::class, SatRadio::class], version = 2) abstract class MainDatabase : RoomDatabase() { abstract fun entriesDao(): EntriesDao diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt index 0c934286..286c5402 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesScreen.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.R import com.rtbishop.look4sat.model.DataState import com.rtbishop.look4sat.model.SatItem @@ -31,7 +31,8 @@ import com.rtbishop.look4sat.presentation.dialogs.TypesDialog import com.rtbishop.look4sat.presentation.onClick @Composable -fun EntriesScreen(navToPasses: () -> Unit, viewModel: EntriesViewModel = hiltViewModel()) { +fun EntriesScreen(navToPasses: () -> Unit) { + val viewModel = viewModel(EntriesViewModel::class.java, factory = EntriesViewModel.Factory) val state = viewModel.satData.collectAsState(initial = DataState.Loading) val showDialog = rememberSaveable { mutableStateOf(false) } @@ -227,6 +228,7 @@ fun EntriesCard(state: DataState>, onSelected: (List, Boolean } } } + else -> CardLoadingIndicator() } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt index 971195c7..8e5cb85b 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/entries/EntriesViewModel.kt @@ -18,23 +18,24 @@ package com.rtbishop.look4sat.presentation.entries import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.rtbishop.look4sat.MainApplication import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.ISatelliteRepository import com.rtbishop.look4sat.domain.ISettingsRepository import com.rtbishop.look4sat.model.DataState import com.rtbishop.look4sat.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( +class EntriesViewModel( private val dataRepository: IDataRepository, private val satelliteRepository: ISatelliteRepository, private val settingsRepository: ISettingsRepository @@ -113,4 +114,18 @@ class EntriesViewModel @Inject constructor( } } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY + initializer { + val container = (this[applicationKey] as MainApplication).container + EntriesViewModel( + container.dataRepository, + container.satelliteRepository, + container.settingsRepository + ) + } + } + } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt index bda84cce..45788de7 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapScreen.kt @@ -1,7 +1,13 @@ package com.rtbishop.look4sat.presentation.map import android.content.Context -import android.graphics.* +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint +import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.compose.foundation.layout.Arrangement @@ -19,9 +25,9 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.ContextCompat -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.R import com.rtbishop.look4sat.domain.Satellite import com.rtbishop.look4sat.model.GeoPos @@ -59,7 +65,8 @@ private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { private val labelRect = Rect() @Composable -fun MapScreen(viewModel: MapViewModel = hiltViewModel()) { +fun MapScreen() { + val viewModel = viewModel(MapViewModel::class.java, factory = MapViewModel.Factory) val context = LocalContext.current val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE) Configuration.getInstance().load(context, preferences) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapViewModel.kt index aefcc08f..4a0401b1 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/map/MapViewModel.kt @@ -18,7 +18,11 @@ package com.rtbishop.look4sat.presentation.map import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.rtbishop.look4sat.MainApplication import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.ISatelliteRepository import com.rtbishop.look4sat.domain.ISettingsRepository @@ -28,19 +32,20 @@ import com.rtbishop.look4sat.model.SatPos import com.rtbishop.look4sat.utility.QthConverter import com.rtbishop.look4sat.utility.toDegrees import com.rtbishop.look4sat.utility.toTimerString -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.* +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.flow -import java.util.* -import javax.inject.Inject +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import java.util.Date import kotlin.collections.set import kotlin.math.max import kotlin.math.min -@HiltViewModel -class MapViewModel @Inject constructor( +class MapViewModel( private val dataRepository: IDataRepository, private val satelliteRepository: ISatelliteRepository, private val settingsRepository: ISettingsRepository @@ -221,4 +226,18 @@ class MapViewModel @Inject constructor( private fun clip(currentValue: Double, minValue: Double, maxValue: Double): Double { return min(max(currentValue, minValue), maxValue) } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY + initializer { + val container = (this[applicationKey] as MainApplication).container + MapViewModel( + container.dataRepository, + container.satelliteRepository, + container.settingsRepository + ) + } + } + } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt index a27d5e38..8f8cd64d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesScreen.kt @@ -20,7 +20,7 @@ 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.* -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.R import com.rtbishop.look4sat.domain.NearEarthSatellite import com.rtbishop.look4sat.model.DataState @@ -38,7 +38,8 @@ import java.util.* private val sdf = SimpleDateFormat("HH:mm:ss", Locale.ENGLISH) @Composable -fun PassesScreen(navToRadar: (Int, Long) -> Unit, viewModel: PassesViewModel = hiltViewModel()) { +fun PassesScreen(navToRadar: (Int, Long) -> Unit) { + val viewModel = viewModel(PassesViewModel::class.java, factory = PassesViewModel.Factory) val state = viewModel.passes.collectAsState(initial = null) val timerText = viewModel.timerText.collectAsState(initial = null) val isRefreshing = state.value is DataState.Loading @@ -102,7 +103,7 @@ private fun PassPreview() { ) val satellite = NearEarthSatellite(data) val pass = SatPass(1L, 25.0, 10L, 75.0, 850, 45.0, satellite, 0.5f) - MainTheme { Pass(pass = pass, { _,_ -> }) } + MainTheme { Pass(pass = pass, { _, _ -> }) } } @Composable diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt index 4df8c25a..e8e1b62d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passes/PassesViewModel.kt @@ -18,21 +18,26 @@ package com.rtbishop.look4sat.presentation.passes import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.rtbishop.look4sat.MainApplication import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.ISatelliteRepository import com.rtbishop.look4sat.domain.ISettingsRepository import com.rtbishop.look4sat.model.DataState import com.rtbishop.look4sat.model.SatPass import com.rtbishop.look4sat.utility.toTimerString -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.* +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import javax.inject.Inject +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch -@HiltViewModel -class PassesViewModel @Inject constructor( +class PassesViewModel( private val dataRepository: IDataRepository, private val satelliteRepository: ISatelliteRepository, private val settingsRepository: ISettingsRepository @@ -113,4 +118,18 @@ class PassesViewModel @Inject constructor( ) } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY + initializer { + val container = (this[applicationKey] as MainApplication).container + PassesViewModel( + container.dataRepository, + container.satelliteRepository, + container.settingsRepository + ) + } + } + } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt index 429cb8f8..11bac330 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarScreen.kt @@ -1,10 +1,21 @@ package com.rtbishop.look4sat.presentation.radar import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.* +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment @@ -16,12 +27,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.R import com.rtbishop.look4sat.model.SatRadio @Composable -fun RadarScreen(viewModel: RadarViewModel = hiltViewModel()) { +fun RadarScreen() { + val viewModel = viewModel(RadarViewModel::class.java, factory = RadarViewModel.Factory) val currentPass = viewModel.getPass().collectAsState(null) Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { @@ -55,9 +67,11 @@ fun RadarScreen(viewModel: RadarViewModel = hiltViewModel()) { ) } } - ElevatedCard(modifier = Modifier - .fillMaxSize() - .weight(1f)) { + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .weight(1f) + ) { viewModel.radarData.value?.let { data -> RadarViewCompose( item = data.satPos, @@ -66,9 +80,11 @@ fun RadarScreen(viewModel: RadarViewModel = hiltViewModel()) { ) } } - ElevatedCard(modifier = Modifier - .fillMaxSize() - .weight(1f)) { + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .weight(1f) + ) { TransmittersList(transmitters = viewModel.transmitters.value) } } @@ -93,7 +109,10 @@ private fun TransmitterItem(radio: SatRadio) { .background(MaterialTheme.colorScheme.surface) .padding(6.dp) ) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { Icon( painter = painterResource(id = R.drawable.ic_next), contentDescription = null, modifier = Modifier.rotate(180f) diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarViewModel.kt index d6cc8c62..d55d8421 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/radar/RadarViewModel.kt @@ -22,8 +22,17 @@ import android.util.Log import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewModelScope -import com.rtbishop.look4sat.domain.* +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.rtbishop.look4sat.MainApplication +import com.rtbishop.look4sat.domain.IDataRepository +import com.rtbishop.look4sat.domain.ISatelliteRepository +import com.rtbishop.look4sat.domain.ISensorRepository +import com.rtbishop.look4sat.domain.ISettingsRepository +import com.rtbishop.look4sat.domain.Satellite import com.rtbishop.look4sat.framework.BluetoothReporter import com.rtbishop.look4sat.framework.NetworkReporter import com.rtbishop.look4sat.model.GeoPos @@ -32,15 +41,12 @@ import com.rtbishop.look4sat.model.SatPos import com.rtbishop.look4sat.model.SatRadio import com.rtbishop.look4sat.utility.round import com.rtbishop.look4sat.utility.toDegrees -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.isActive import kotlinx.coroutines.launch -import javax.inject.Inject -@HiltViewModel -class RadarViewModel @Inject constructor( +class RadarViewModel( private val bluetoothReporter: BluetoothReporter, private val networkReporter: NetworkReporter, private val savedStateHandle: SavedStateHandle, @@ -149,4 +155,23 @@ class RadarViewModel @Inject constructor( transmitters.value = list } } + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY + initializer { + val savedStateHandle = createSavedStateHandle() + val container = (this[applicationKey] as MainApplication).container + RadarViewModel( + container.bluetoothReporter, + container.networkReporter, + savedStateHandle, + container.dataRepository, + container.satelliteRepository, + container.sensorRepository, + container.settingsRepository + ) + } + } + } } diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt index 95113885..b5fffb3a 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsScreen.kt @@ -4,9 +4,22 @@ import android.Manifest import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.* +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Switch +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf @@ -21,7 +34,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.viewmodel.compose.viewModel import com.rtbishop.look4sat.BuildConfig import com.rtbishop.look4sat.R import com.rtbishop.look4sat.model.GeoPos @@ -32,7 +45,8 @@ import com.rtbishop.look4sat.presentation.dialogs.PositionDialog import com.rtbishop.look4sat.presentation.gotoUrl import com.rtbishop.look4sat.presentation.showToast import java.text.SimpleDateFormat -import java.util.* +import java.util.Date +import java.util.Locale private const val POLICY_URL = "https://sites.google.com/view/look4sat-privacy-policy/home" private const val LICENSE_URL = "https://www.gnu.org/licenses/gpl-3.0.html" @@ -41,7 +55,8 @@ private const val DONATE_URL = "https://ko-fi.com/rt_bishop" private const val FDROID_URL = "https://f-droid.org/en/packages/com.rtbishop.look4sat/" @Composable -fun SettingsScreen(viewModel: SettingsViewModel = hiltViewModel()) { +fun SettingsScreen() { + val viewModel = viewModel(SettingsViewModel::class.java, factory = SettingsViewModel.Factory) val context = LocalContext.current // Permissions setup diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt index 92839665..c176d842 100644 --- a/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/presentation/settings/SettingsViewModel.kt @@ -19,20 +19,20 @@ package com.rtbishop.look4sat.presentation.settings import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.rtbishop.look4sat.MainApplication import com.rtbishop.look4sat.R import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.ISettingsRepository import com.rtbishop.look4sat.model.DataState -import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -import javax.inject.Inject -@HiltViewModel -class SettingsViewModel @Inject constructor( - private val dataRepository: IDataRepository, - private val settingsRepository: ISettingsRepository +class SettingsViewModel( + private val dataRepository: IDataRepository, private val settingsRepository: ISettingsRepository ) : ViewModel() { private val defaultLocationSettings = LocationSettings(false, @@ -157,4 +157,14 @@ class SettingsViewModel @Inject constructor( // fun setBTDeviceName(value: String) = settings.setBTDeviceName(value) // fun getBTDeviceAddr(): String = settings.getBTDeviceAddr() // fun setBTDeviceAddr(value: String) = settings.setBTDeviceAddr(value) + + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + val applicationKey = ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY + initializer { + val container = (this[applicationKey] as MainApplication).container + SettingsViewModel(container.dataRepository, container.settingsRepository) + } + } + } } diff --git a/build.gradle b/build.gradle index c665e708..e5ae64cf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,9 @@ buildscript { ext { - application_version = '7.4.2' - library_version = '7.1.2' + gradle_plugin_version = '7.4.2' + library_plugin_version = '7.1.2' + ksp_version = '1.8.10-1.0.9' kotlin_android_version = '1.8.10' - hilt_android_version = '2.45' // Android splashscreen_version = '1.0.0' room_version = '2.5.0' @@ -12,28 +12,27 @@ buildscript { compose_version = '1.3.3' compose_compiler_version = '1.4.3' material3_version = '1.0.1' - hilt_compose_version = '1.0.0' - lifecycle_version = '2.5.1' + lifecycle_version = '2.6.0' navigation_version = '2.5.3' // Utility timber_version = '5.0.1' osmdroid_version = '6.1.14' - json_version = '20220924' + json_version = '20230227' leakcanary_version = '2.10' // Test junit_version = '4.13.2' - mockito_version = '5.1.1' + mockito_version = '5.2.0' coroutines_test_version = '1.6.4' } } plugins { - id "com.android.application" version "$application_version" apply false - id "com.android.library" version "$library_version" apply false + id "com.android.application" version "$gradle_plugin_version" apply false + id "com.android.library" version "$library_plugin_version" apply false + id "com.google.devtools.ksp" version "$ksp_version" apply false id "org.jetbrains.kotlin.android" version "$kotlin_android_version" apply false - id "com.google.dagger.hilt.android" version "$hilt_android_version" apply false } -task clean(type: Delete) { +tasks.register('clean', Delete) { delete rootProject.buildDir } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7702c1e8..e4c44573 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Feb 17 17:52:48 GMT 2023 +#Mon Mar 20 10:37:41 GMT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists