mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-04 11:37:22 +00: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
|
||||
|
||||
+14
-8
@@ -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()
|
||||
}
|
||||
}
|
||||
+11
-2
@@ -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(
|
||||
|
||||
+5
-3
@@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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()
|
||||
|
||||
+69
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user