diff --git a/app/build.gradle b/app/build.gradle index 6075fe2f..ad61dce8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,14 +24,15 @@ apply plugin: 'kotlin-kapt' apply plugin: "androidx.navigation.safeargs.kotlin" android { - compileSdkVersion 29 - buildToolsVersion "29.0.3" + compileSdkVersion 30 + buildToolsVersion "30.0.2" defaultConfig { applicationId "com.rtbishop.look4sat" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 30 versionCode 18 versionName '1.4.8' + resConfigs "en", "ru" } buildTypes { release { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/SharedViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/ui/SharedViewModel.kt index af6714ad..5dec5e65 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/SharedViewModel.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/SharedViewModel.kt @@ -22,7 +22,10 @@ package com.rtbishop.look4sat.ui import android.net.Uri import androidx.lifecycle.* import com.github.amsacode.predict4java.GroundStationPosition -import com.rtbishop.look4sat.data.* +import com.rtbishop.look4sat.data.Result +import com.rtbishop.look4sat.data.SatEntry +import com.rtbishop.look4sat.data.SatPass +import com.rtbishop.look4sat.data.TleSource import com.rtbishop.look4sat.repo.Repository import com.rtbishop.look4sat.utility.PassPredictor import com.rtbishop.look4sat.utility.PrefsManager @@ -39,6 +42,7 @@ class SharedViewModel @Inject constructor( ) : ViewModel() { private val _passes = MutableLiveData>>() + private var selectedEntries = emptyList() init { if (prefsManager.isFirstLaunch()) { @@ -46,35 +50,15 @@ class SharedViewModel @Inject constructor( } } + fun setSelectedEntries(entries: List) { + selectedEntries = entries.filter { it.isSelected } + } + fun getSources() = repository.getSources() fun getEntries() = repository.getEntries() fun getPasses(): LiveData>> = _passes fun getTransmittersForSat(id: Int) = liveData { emit(repository.getTransmittersForSatId(id)) } - fun getPassPrefs(): PassPrefs { - return prefsManager.getPassPrefs() - } - - fun setPassPrefs(hoursAhead: Int, minEl: Double) { - prefsManager.setPassPrefs(hoursAhead, minEl) - } - - fun getStationPosition(): GroundStationPosition { - return prefsManager.getStationPosition() - } - - fun setStationPositionFromGPS() { - prefsManager.setStationPositionFromGPS() - } - - fun shouldUseUTC(): Boolean { - return prefsManager.shouldUseUTC() - } - - fun shouldUseCompass(): Boolean { - return prefsManager.shouldUseCompass() - } - fun updateEntriesFromFile(uri: Uri) { viewModelScope.launch { repository.updateEntriesFromFile(uri) @@ -89,31 +73,35 @@ class SharedViewModel @Inject constructor( } } - fun updateEntriesSelection(satIds: MutableList) { + fun updateEntriesSelection(entries: List) { viewModelScope.launch { - repository.updateEntriesSelection(satIds) + selectedEntries = entries.filter { it.isSelected } + calculatePasses() + repository.updateEntriesSelection(selectedEntries.map { it.catNum }) } } - fun calculatePassesForEntries(entries: List) { + fun calculatePasses(dateNow: Date = Date(System.currentTimeMillis())) { _passes.value = Result.InProgress - val dateNow = Date(System.currentTimeMillis()) - val passes = mutableListOf() viewModelScope.launch(Dispatchers.Default) { - entries.forEach { - passes.addAll(getPassesForEntries(it, dateNow, getStationPosition())) + val passes = mutableListOf() + selectedEntries.forEach { + passes.addAll(getPassesForEntries(it, dateNow, prefsManager.getStationPosition())) } - val filteredPasses = filterAndSortPasses(passes, dateNow, getPassPrefs().hoursAhead) + val filteredPasses = + filterAndSortPasses(passes, dateNow, prefsManager.getPassPrefs().hoursAhead) _passes.postValue(Result.Success(filteredPasses)) } } private fun setDefaultTleSources() { - val defaultTleSources = listOf( - TleSource("https://celestrak.com/NORAD/elements/active.txt"), - TleSource("https://amsat.org/tle/current/nasabare.txt") - ) - viewModelScope.launch { repository.updateSources(defaultTleSources) } + viewModelScope.launch { + val defaultTleSources = listOf( + TleSource("https://celestrak.com/NORAD/elements/active.txt"), + TleSource("https://amsat.org/tle/current/nasabare.txt") + ) + repository.updateSources(defaultTleSources) + } } private fun getPassesForEntries( diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/EntriesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/EntriesFragment.kt index b59cd297..84833bb6 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/EntriesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/EntriesFragment.kt @@ -6,7 +6,7 @@ import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.activityViewModels import androidx.navigation.findNavController import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -26,7 +26,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) { lateinit var factory: ViewModelFactory private lateinit var binding: FragmentEntriesBinding - private lateinit var viewModel: SharedViewModel + private val viewModel: SharedViewModel by activityViewModels { factory } private val pickFileReqCode = 100 private val entriesAdapter = EntriesAdapter() private var tleSources = listOf() @@ -35,7 +35,6 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) { super.onViewCreated(view, savedInstanceState) binding = FragmentEntriesBinding.bind(view) (requireActivity().application as Look4SatApp).appComponent.inject(this) - viewModel = ViewModelProvider(requireActivity(), factory).get(SharedViewModel::class.java) setupComponents() setupObservers() } @@ -51,6 +50,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) { layoutManager = linearLayoutMgr adapter = entriesAdapter addItemDecoration(divider) + setHasFixedSize(true) } importWeb.setOnClickListener { showImportFromWebDialog() } importFile.setOnClickListener { showImportFromFileDialog() } @@ -64,6 +64,7 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) { private fun setupObservers() { viewModel.getSources().observe(viewLifecycleOwner, { tleSources = it }) viewModel.getEntries().observe(viewLifecycleOwner, { + viewModel.setSelectedEntries(it) entriesAdapter.setEntries(it as MutableList) }) } @@ -81,9 +82,8 @@ class EntriesFragment : Fragment(R.layout.fragment_entries) { } private fun navigateToPasses() { - val catNumList = mutableListOf() - entriesAdapter.getEntries().forEach { if (it.isSelected) catNumList.add(it.catNum) } - viewModel.updateEntriesSelection(catNumList) + binding.searchBar.clearFocus() + viewModel.updateEntriesSelection(entriesAdapter.getEntries()) requireView().findNavController().navigate(R.id.action_entries_to_passes) } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt index c24cb893..312f9e1d 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt @@ -10,7 +10,7 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import com.github.amsacode.predict4java.GroundStationPosition @@ -22,6 +22,7 @@ import com.rtbishop.look4sat.data.SatItem import com.rtbishop.look4sat.data.SatPass import com.rtbishop.look4sat.databinding.FragmentMapBinding import com.rtbishop.look4sat.ui.SharedViewModel +import com.rtbishop.look4sat.utility.PrefsManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -42,22 +43,24 @@ import kotlin.math.sqrt class MapFragment : Fragment(R.layout.fragment_map) { @Inject - lateinit var modelFactory: ViewModelFactory + lateinit var factory: ViewModelFactory + + @Inject + lateinit var prefsManager: PrefsManager private lateinit var mainActivity: FragmentActivity - private lateinit var viewModel: SharedViewModel private lateinit var binding: FragmentMapBinding private lateinit var trackPaint: Paint private lateinit var footprintPaint: Paint private lateinit var selectedPass: SatPass private val dateNow = Date(System.currentTimeMillis()) + private val viewModel: SharedViewModel by activityViewModels { factory } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentMapBinding.bind(view) mainActivity = requireActivity() (mainActivity.application as Look4SatApp).appComponent.inject(this) - viewModel = ViewModelProvider(mainActivity, modelFactory).get(SharedViewModel::class.java) val prefs = PreferenceManager.getDefaultSharedPreferences(mainActivity.applicationContext) Configuration.getInstance().load(mainActivity.applicationContext, prefs) @@ -78,7 +81,7 @@ class MapFragment : Fragment(R.layout.fragment_map) { } setupMapView() - setupPosOverlay(viewModel.getStationPosition()) + setupPosOverlay(prefsManager.getStationPosition()) setupObservers() } diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PassesFragment.kt index c92c15f2..03fe8661 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PassesFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PassesFragment.kt @@ -19,25 +19,27 @@ package com.rtbishop.look4sat.ui.fragments +import android.animation.ObjectAnimator import android.os.Bundle import android.os.CountDownTimer import android.view.View import android.widget.EditText import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator import com.rtbishop.look4sat.Look4SatApp import com.rtbishop.look4sat.R import com.rtbishop.look4sat.dagger.ViewModelFactory import com.rtbishop.look4sat.data.Result -import com.rtbishop.look4sat.data.SatEntry import com.rtbishop.look4sat.data.SatPass import com.rtbishop.look4sat.databinding.FragmentPassesBinding import com.rtbishop.look4sat.ui.SharedViewModel import com.rtbishop.look4sat.ui.adapters.PassesAdapter +import com.rtbishop.look4sat.utility.PrefsManager import com.rtbishop.look4sat.utility.Utilities +import com.rtbishop.look4sat.utility.Utilities.getRotationAnimator import com.rtbishop.look4sat.utility.Utilities.snack import java.util.* import javax.inject.Inject @@ -47,44 +49,35 @@ class PassesFragment : Fragment(R.layout.fragment_passes) { @Inject lateinit var factory: ViewModelFactory - private lateinit var viewModel: SharedViewModel + @Inject + lateinit var prefsManager: PrefsManager + private lateinit var aosTimer: CountDownTimer private lateinit var binding: FragmentPassesBinding - private var isTimerSet: Boolean = false + private lateinit var animator: ObjectAnimator + private val viewModel: SharedViewModel by activityViewModels { factory } private val passesAdapter = PassesAdapter() - private var selectedEntries = listOf() + private var isTimerSet: Boolean = false private var passes = mutableListOf() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentPassesBinding.bind(view) (requireActivity().application as Look4SatApp).appComponent.inject(this) - viewModel = ViewModelProvider(requireActivity(), factory).get(SharedViewModel::class.java) - setupObservers() setupComponents() + setupObservers() } -// override fun onStart() { -// super.onStart() -// if (selectedEntries.isEmpty()) { -// viewModel.calculatePassesForEntries(selectedEntries) -// } -// } - private fun setupObservers() { - viewModel.getEntries().observe(viewLifecycleOwner, { allEntries -> - selectedEntries = allEntries.filter { it.isSelected } -// viewModel.calculatePassesForEntries(selectedEntries) - }) viewModel.getPasses().observe(viewLifecycleOwner, { result -> when (result) { is Result.Success -> { + animator.cancel() passes = result.data passesAdapter.setList(passes) setTimerForPasses(passes) } - is Result.InProgress -> { - } + is Result.InProgress -> animator.start() } }) } @@ -96,15 +89,22 @@ class PassesFragment : Fragment(R.layout.fragment_passes) { adapter = passesAdapter isVerticalScrollBarEnabled = false (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false + setHasFixedSize(true) } passesFilter.setOnClickListener { - val passPrefs = viewModel.getPassPrefs() + val passPrefs = prefsManager.getPassPrefs() showSatPassPrefsDialog(passPrefs.hoursAhead, passPrefs.minEl) } - passesFab.setOnClickListener { viewModel.calculatePassesForEntries(selectedEntries) } + animator = passesFab.getRotationAnimator() + passesFab.setOnClickListener { + if (animator.isRunning) { + animator.cancel() + } else { + viewModel.calculatePasses() + } + } } - passesAdapter.setShouldUseUTC(viewModel.shouldUseUTC()) - passesAdapter.setList(passes) + passesAdapter.setShouldUseUTC(prefsManager.shouldUseUTC()) setTimerForPasses(passes) } @@ -130,8 +130,8 @@ class PassesFragment : Fragment(R.layout.fragment_passes) { getString(R.string.pref_min_el_input_error).snack(requireView()) } else -> { - viewModel.setPassPrefs(hours, elevation) - viewModel.calculatePassesForEntries(selectedEntries) + prefsManager.setPassPrefs(hours, elevation) + viewModel.calculatePasses() } } } else getString(R.string.err_enter_value).snack(requireView()) @@ -183,7 +183,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes) { val millisBeforeEnd = lastPass.pass.endTime.time.minus(timeNow.time) aosTimer = object : CountDownTimer(millisBeforeEnd, 1000) { override fun onFinish() { - viewModel.calculatePassesForEntries(selectedEntries) + viewModel.calculatePasses() } override fun onTick(millisUntilFinished: Long) { diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PolarFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PolarFragment.kt index e8227c36..bdd2e5ab 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PolarFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/PolarFragment.kt @@ -25,7 +25,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager @@ -38,6 +38,7 @@ import com.rtbishop.look4sat.databinding.FragmentPolarBinding import com.rtbishop.look4sat.ui.SharedViewModel import com.rtbishop.look4sat.ui.adapters.TransAdapter import com.rtbishop.look4sat.ui.views.PolarView +import com.rtbishop.look4sat.utility.PrefsManager import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.* @@ -47,24 +48,26 @@ import kotlin.math.round class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { @Inject - lateinit var modelFactory: ViewModelFactory + lateinit var factory: ViewModelFactory + + @Inject + lateinit var prefsManager: PrefsManager private lateinit var mainActivity: FragmentActivity - private lateinit var viewModel: SharedViewModel private lateinit var binding: FragmentPolarBinding private lateinit var satPass: SatPass private lateinit var sensorManager: SensorManager - private var polarView: PolarView? = null + private val viewModel: SharedViewModel by activityViewModels { factory } private val args: PolarFragmentArgs by navArgs() private val transmitterAdapter = TransAdapter() private var magneticDeclination = 0f + private var polarView: PolarView? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mainActivity = requireActivity() binding = FragmentPolarBinding.bind(view) (mainActivity.application as Look4SatApp).appComponent.inject(this) - viewModel = ViewModelProvider(mainActivity, modelFactory).get(SharedViewModel::class.java) sensorManager = mainActivity.getSystemService(Context.SENSOR_SERVICE) as SensorManager binding.recycler.apply { layoutManager = LinearLayoutManager(mainActivity) @@ -76,7 +79,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { override fun onResume() { super.onResume() - if (viewModel.shouldUseCompass()) { + if (prefsManager.shouldUseCompass()) { sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR).also { sensor -> sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL) } @@ -85,7 +88,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { override fun onPause() { super.onPause() - if (viewModel.shouldUseCompass()) { + if (prefsManager.shouldUseCompass()) { sensorManager.unregisterListener(this) } } @@ -134,7 +137,7 @@ class PolarFragment : Fragment(R.layout.fragment_polar), SensorEventListener { } private fun calculateMagneticDeclination() { - val position = viewModel.getStationPosition() + val position = prefsManager.getStationPosition() val geoField = GeomagneticField( position.latitude.toFloat(), position.longitude.toFloat(), diff --git a/app/src/main/java/com/rtbishop/look4sat/utility/Utilities.kt b/app/src/main/java/com/rtbishop/look4sat/utility/Utilities.kt index fc9e3b5b..fd521fbe 100644 --- a/app/src/main/java/com/rtbishop/look4sat/utility/Utilities.kt +++ b/app/src/main/java/com/rtbishop/look4sat/utility/Utilities.kt @@ -30,7 +30,7 @@ object Utilities { fun View.getRotationAnimator(): ObjectAnimator { return ObjectAnimator.ofFloat(this, "rotation", 0f, 360f).apply { - duration = 800 + duration = 333 repeatCount = ObjectAnimator.INFINITE repeatMode = ObjectAnimator.RESTART interpolator = LinearInterpolator() diff --git a/build.gradle b/build.gradle index 7302e4ef..f1376742 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ buildscript { gradle_plugin_version = '1.4.10' material_version = '1.2.1' constraint_layout_version = '2.0.1' - ktx_version = '1.3.1' + ktx_version = '1.3.2' preference_version = '1.1.1' navigation_version = '2.3.0' lifecycle_version = '2.2.0'