Reverted TLE source update screen, fixed location setting bug #53 #54

This commit is contained in:
Arty Bishop committed 2021-05-18 10:24:26 +01:00
1 parent fb2008b07a
commit a0a95ce372
26 files changed
+404 -78

No files matched your search

@@ -25,15 +25,49 @@ import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.domain.predict4kotlin.QthConverter
import com.rtbishop.look4sat.domain.predict4kotlin.StationPosition
import com.rtbishop.look4sat.utility.round
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import javax.inject.Inject
class PreferencesProvider @Inject constructor(
moshi: Moshi,
private val qthConverter: QthConverter,
private val locationManager: LocationManager,
private val preferences: SharedPreferences
) : PreferencesSource {
private val sourcesType = Types.newParameterizedType(List::class.java, String::class.java)
private val sourcesAdapter = moshi.adapter<List<String>>(sourcesType)
override fun loadTleSources(): List<String> {
return try {
val sourcesString = preferences.getString(keySources, String())
if (sourcesString.isNullOrEmpty()) {
loadDefaultSources()
} else {
sourcesAdapter.fromJson(sourcesString) ?: loadDefaultSources()
}
} catch (exception: ClassCastException) {
loadDefaultSources()
}
}
override fun saveTleSources(sources: List<String>) {
val sourcesJson = sourcesAdapter.toJson(sources)
preferences.edit { putString(keySources, sourcesJson) }
}
override fun loadDefaultSources(): List<String> {
return listOf(
"https://celestrak.com/NORAD/elements/active.txt",
"https://amsat.org/tle/current/nasabare.txt",
"https://www.prismnet.com/~mmccants/tles/classfd.zip",
"https://www.prismnet.com/~mmccants/tles/inttles.zip"
)
}
companion object {
const val keySources = "sourcesListJson"
const val keyModes = "satModes"
const val keyCompass = "compass"
const val keyTextLabels = "shouldUseTextLabels"
@@ -27,9 +27,9 @@ import com.rtbishop.look4sat.framework.PreferencesProvider
import com.rtbishop.look4sat.framework.api.SatDataRemote
import com.rtbishop.look4sat.framework.api.SatDataService
import com.rtbishop.look4sat.framework.db.RoomConverters
import com.rtbishop.look4sat.framework.db.SatDataLocal
import com.rtbishop.look4sat.framework.db.SatDataDao
import com.rtbishop.look4sat.framework.db.SatDataDb
import com.rtbishop.look4sat.framework.db.SatDataLocal
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
@@ -58,11 +58,12 @@ object SatDataModule {
@Provides
@Singleton
fun providePreferenceSource(
moshi: Moshi,
qthConverter: QthConverter,
locationManager: LocationManager,
preferences: SharedPreferences
): PreferencesSource {
return PreferencesProvider(qthConverter, locationManager, preferences)
return PreferencesProvider(moshi, qthConverter, locationManager, preferences)
}
@Provides
@@ -28,10 +28,10 @@ import androidx.core.content.ContextCompat
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.framework.PreferencesProvider
import com.rtbishop.look4sat.utility.showSnack
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@@ -46,7 +46,7 @@ class AppSettingsFragment : PreferenceFragmentCompat() {
if (isGranted) {
updatePositionFromGPS()
} else {
requireView().showSnack(getString(R.string.pref_pos_gps_error))
showSnack(getString(R.string.pref_pos_gps_error))
}
}
@@ -75,7 +75,7 @@ class AppSettingsFragment : PreferenceFragmentCompat() {
if (Patterns.IP_ADDRESS.matcher(newValue.toString()).matches()) {
return@setOnPreferenceChangeListener true
} else {
requireView().showSnack(getString(R.string.tracking_rotator_address_invalid))
showSnack(getString(R.string.tracking_rotator_address_invalid))
return@setOnPreferenceChangeListener false
}
}
@@ -88,7 +88,7 @@ class AppSettingsFragment : PreferenceFragmentCompat() {
if (portValue.isNotEmpty() && portValue.toInt() in 1024..65535) {
return@setOnPreferenceChangeListener true
} else {
requireView().showSnack(getString(R.string.tracking_rotator_port_invalid))
showSnack(getString(R.string.tracking_rotator_port_invalid))
return@setOnPreferenceChangeListener false
}
}
@@ -97,10 +97,10 @@ class AppSettingsFragment : PreferenceFragmentCompat() {
private fun updatePositionFromQth(qthString: String): Boolean {
return if (preferencesSource.updatePositionFromQTH(qthString)) {
requireView().showSnack(getString(R.string.pref_pos_success))
showSnack(getString(R.string.pref_pos_success))
true
} else {
requireView().showSnack(getString(R.string.pref_pos_qth_error))
showSnack(getString(R.string.pref_pos_qth_error))
false
}
}
@@ -110,10 +110,16 @@ class AppSettingsFragment : PreferenceFragmentCompat() {
val locPermResult = ContextCompat.checkSelfPermission(requireContext(), locPermString)
if (locPermResult == PackageManager.PERMISSION_GRANTED) {
if (preferencesSource.updatePositionFromGPS()) {
requireView().showSnack(getString(R.string.pref_pos_success))
showSnack(getString(R.string.pref_pos_success))
} else {
requireView().showSnack(getString(R.string.pref_pos_gps_null))
showSnack(getString(R.string.pref_pos_gps_null))
}
} else requestPermissionLauncher.launch(locPermString)
}
private fun showSnack(message: String) {
Snackbar.make(requireView(), message, Snackbar.LENGTH_SHORT).apply {
setAnchorView(R.id.nav_bottom)
}.show()
}
}
@@ -24,6 +24,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -33,6 +34,8 @@ import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.framework.model.Result
import com.rtbishop.look4sat.utility.RecyclerDivider
import com.rtbishop.look4sat.utility.getNavResult
import com.rtbishop.look4sat.utility.navigateSafe
import com.rtbishop.look4sat.utility.showSnack
import dagger.hilt.android.AndroidEntryPoint
@@ -62,8 +65,11 @@ class SatItemFragment : Fragment(R.layout.fragment_entries) {
(itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
addItemDecoration(RecyclerDivider(R.drawable.rec_divider_light))
}
importWeb.setOnClickListener { viewModel.updateEntriesFromWeb(null) }
importSource.setOnClickListener { showSourceDialog() }
importWeb.setOnClickListener {
// viewModel.updateEntriesFromWeb(null)
findNavController().navigateSafe(R.id.action_entries_to_sources)
}
// importSource.setOnClickListener { showSourceDialog() }
importFile.setOnClickListener { filePicker.launch("*/*") }
selectMode.setOnClickListener { showModesDialog() }
selectAll.setOnClickListener { viewModel.selectCurrentItems() }
@@ -72,6 +78,9 @@ class SatItemFragment : Fragment(R.layout.fragment_entries) {
viewModel.satData.observe(viewLifecycleOwner, { satData ->
handleSatData(satData, binding, entriesAdapter)
})
getNavResult<List<String>>(R.id.nav_entries, "sources") { sources ->
viewModel.updateEntriesFromWeb(sources)
}
}
private fun handleSatData(
@@ -21,6 +21,7 @@ import android.content.ContentResolver
import android.net.Uri
import android.widget.SearchView
import androidx.lifecycle.*
import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.data.SatDataRepository
import com.rtbishop.look4sat.domain.model.SatItem
import com.rtbishop.look4sat.framework.model.Result
@@ -32,6 +33,7 @@ import javax.inject.Inject
@HiltViewModel
class SatItemViewModel @Inject constructor(
private val preferencesSource: PreferencesSource,
private val resolver: ContentResolver,
private val satDataRepository: SatDataRepository,
) : ViewModel(), SatItemAdapter.EntriesClickListener, SearchView.OnQueryTextListener {
@@ -61,12 +63,12 @@ class SatItemViewModel @Inject constructor(
}
}
fun updateEntriesFromWeb(sources: List<String>?) {
fun updateEntriesFromWeb(sources: List<String>) {
viewModelScope.launch {
_satData.value = Result.InProgress
runCatching {
if (sources == null) satDataRepository.updateEntriesFromWeb()
else satDataRepository.updateEntriesFromWeb(sources)
preferencesSource.saveTleSources(sources)
satDataRepository.updateEntriesFromWeb(sources)
}.onFailure { _satData.value = Result.Error(it) }
}
}
@@ -65,8 +65,8 @@ class SatPassViewModel @Inject constructor(
preferencesSource.updatePositionFromGPS()
viewModelScope.launch {
_passes.postValue(Result.InProgress)
satDataRepository.updateEntriesFromWeb()
val defaultCatNums = listOf(43700, 25544, 25338, 28654, 33591, 40069, 27607, 24278)
satDataRepository.updateEntriesFromWeb(preferencesSource.loadDefaultSources())
satDataRepository.updateEntriesSelection(defaultCatNums, true)
satPassRepository.forceCalculation(satDataRepository.getSelectedSatellites())
preferencesSource.setSetupDone()
@@ -0,0 +1,69 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.presentation.satSourcesScreen
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.widget.doOnTextChanged
import androidx.recyclerview.widget.RecyclerView
import com.rtbishop.look4sat.databinding.ItemTleSourceBinding
class SourcesAdapter(private val sources: MutableList<TleSource> = mutableListOf()) :
RecyclerView.Adapter<SourcesAdapter.TleSourceHolder>() {
fun getSources(): List<TleSource> {
return sources.filter { it.url.contains("https://") }
}
fun setSources(list: List<TleSource>) {
sources.clear()
sources.addAll(list)
}
fun addSource() {
sources.add(TleSource())
notifyItemInserted(itemCount - 1)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TleSourceHolder {
val binding = ItemTleSourceBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return TleSourceHolder(binding)
}
override fun onBindViewHolder(holder: TleSourceHolder, position: Int) {
holder.bind(sources[position])
}
override fun getItemCount(): Int {
return sources.size
}
inner class TleSourceHolder(private val binding: ItemTleSourceBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(source: TleSource) {
binding.tleSourceUrl.setText(source.url)
binding.tleSourceUrl.doOnTextChanged { text, _, _, _ -> source.url = text.toString() }
binding.tleSourceInputLayout.setEndIconOnClickListener {
sources.remove(source)
notifyItemRemoved(adapterPosition)
}
}
}
}
@@ -0,0 +1,67 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.presentation.satSourcesScreen
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.PreferencesSource
import com.rtbishop.look4sat.databinding.DialogSourcesBinding
import com.rtbishop.look4sat.utility.setNavResult
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class SourcesDialog : AppCompatDialogFragment() {
@Inject
lateinit var prefsManager: PreferencesSource
override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
return inflater.inflate(R.layout.dialog_sources, group, false)
}
override fun onViewCreated(view: View, state: Bundle?) {
super.onViewCreated(view, state)
val sources = prefsManager.loadTleSources().map { TleSource(it) }
val sourcesAdapter = SourcesAdapter().apply { setSources(sources) }
DialogSourcesBinding.bind(view).apply {
dialog?.window?.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.WRAP_CONTENT
)
sourcesRecycler.apply {
adapter = sourcesAdapter
layoutManager = LinearLayoutManager(requireContext())
}
sourceBtnAdd.setOnClickListener {
sourcesAdapter.addSource()
}
sourcesBtnPos.setOnClickListener {
setNavResult("sources", sourcesAdapter.getSources().map { it.url })
dismiss()
}
sourcesBtnNeg.setOnClickListener { dismiss() }
}
}
}
@@ -0,0 +1,20 @@
/*
* Look4Sat. Amateur radio satellite tracker and pass predictor.
* Copyright (C) 2019-2021 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 <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.presentation.satSourcesScreen
class TleSource(var url: String = String())
@@ -20,9 +20,13 @@ package com.rtbishop.look4sat.utility
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.NavController
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.rtbishop.look4sat.R
import java.util.concurrent.TimeUnit
@@ -58,3 +62,26 @@ fun NavController.navigateSafe(
navigate(resId, args, navOptions, navExtras)
}
}
fun <T> Fragment.setNavResult(key: String, value: T) {
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, value)
}
fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
val navBackStackEntry = findNavController().getBackStackEntry(id)
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME
&& navBackStackEntry.savedStateHandle.contains(key)
) {
val result = navBackStackEntry.savedStateHandle.get<T>(key)
result?.let(onResult)
navBackStackEntry.savedStateHandle.remove<T>(key)
}
}
navBackStackEntry.lifecycle.addObserver(observer)
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
navBackStackEntry.lifecycle.removeObserver(observer)
}
})
}