v2.5.0 - Minor fixes, updated screenshots, version bumped
|
@ -13,8 +13,8 @@ android {
|
|||
applicationId "com.rtbishop.look4sat"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
versionCode 240
|
||||
versionName "2.4.0"
|
||||
versionCode 250
|
||||
versionName "2.5.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.DialogSourceBinding
|
||||
import com.rtbishop.look4sat.databinding.FragmentEntriesBinding
|
||||
|
@ -64,7 +65,7 @@ class SatItemFragment : Fragment(R.layout.fragment_entries) {
|
|||
importWeb.setOnClickListener { viewModel.updateEntriesFromWeb(null) }
|
||||
importSource.setOnClickListener { showSourceDialog() }
|
||||
importFile.setOnClickListener { filePicker.launch("*/*") }
|
||||
selectMode.setOnClickListener { viewModel.createModesDialog(requireContext()).show() }
|
||||
selectMode.setOnClickListener { showModesDialog() }
|
||||
selectAll.setOnClickListener { viewModel.selectCurrentItems() }
|
||||
searchBar.setOnQueryTextListener(viewModel)
|
||||
}
|
||||
|
@ -114,4 +115,33 @@ class SatItemFragment : Fragment(R.layout.fragment_entries) {
|
|||
}
|
||||
dialogBuilder.create().show()
|
||||
}
|
||||
|
||||
private fun showModesDialog() {
|
||||
val modes = arrayOf(
|
||||
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||
"CERTO", "CW", "DQPSK", "DSTAR", "DUV", "FFSK", "FM", "FMN", "FSK", "FSK AX.100 Mode 5",
|
||||
"FSK AX.100 Mode 6", "FSK AX.25 G3RUH", "GFSK", "GFSK Rktr", "GMSK", "HRPT", "LoRa",
|
||||
"LRPT", "LSB", "MFSK", "MSK", "MSK AX.100 Mode 5", "MSK AX.100 Mode 6", "OFDM", "OQPSK",
|
||||
"PSK", "PSK31", "PSK63", "QPSK", "QPSK31", "QPSK63", "SSTV", "USB", "WSJT"
|
||||
)
|
||||
val savedModes = BooleanArray(modes.size)
|
||||
val selectedModes = viewModel.loadSelectedModes().toMutableList()
|
||||
selectedModes.forEach { savedModes[modes.indexOf(it)] = true }
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(context.getString(R.string.modes_title))
|
||||
setMultiChoiceItems(modes, savedModes) { _, which, isChecked ->
|
||||
when {
|
||||
isChecked -> selectedModes.add(modes[which])
|
||||
selectedModes.contains(modes[which]) -> selectedModes.remove(modes[which])
|
||||
}
|
||||
}
|
||||
setPositiveButton(context.getString(android.R.string.ok)) { _, _ ->
|
||||
viewModel.saveSelectedModes(selectedModes)
|
||||
}
|
||||
setNeutralButton(context.getString(android.R.string.cancel)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
dialogBuilder.create().show()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,9 @@
|
|||
package com.rtbishop.look4sat.presentation.satItemScreen
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.widget.SearchView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.*
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.data.SatDataRepository
|
||||
import com.rtbishop.look4sat.domain.model.SatItem
|
||||
import com.rtbishop.look4sat.framework.model.Result
|
||||
|
@ -40,7 +36,7 @@ class SatItemViewModel @Inject constructor(
|
|||
private val satDataRepository: SatDataRepository,
|
||||
) : ViewModel(), SatItemAdapter.EntriesClickListener, SearchView.OnQueryTextListener {
|
||||
|
||||
private val transModes = MutableLiveData(satDataRepository.loadModesSelection())
|
||||
private val transModes = MutableLiveData(satDataRepository.loadSelectedModes())
|
||||
private val currentQuery = MutableLiveData(String())
|
||||
private val itemsWithModes = transModes.switchMap { modes ->
|
||||
liveData { satDataRepository.getSatItems().collect { emit(filterByModes(it, modes)) } }
|
||||
|
@ -83,28 +79,13 @@ class SatItemViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun createModesDialog(context: Context): AlertDialog {
|
||||
val modes = satDataRepository.getAllModes()
|
||||
val savedModes = BooleanArray(modes.size)
|
||||
val selectedModes = satDataRepository.loadModesSelection().toMutableList()
|
||||
selectedModes.forEach { savedModes[modes.indexOf(it)] = true }
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(context).apply {
|
||||
setTitle(context.getString(R.string.modes_title))
|
||||
setMultiChoiceItems(modes, savedModes) { _, which, isChecked ->
|
||||
when {
|
||||
isChecked -> selectedModes.add(modes[which])
|
||||
selectedModes.contains(modes[which]) -> selectedModes.remove(modes[which])
|
||||
}
|
||||
}
|
||||
setPositiveButton(context.getString(android.R.string.ok)) { _, _ ->
|
||||
transModes.value = selectedModes
|
||||
satDataRepository.saveModesSelection(selectedModes)
|
||||
}
|
||||
setNeutralButton(context.getString(android.R.string.cancel)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
return dialogBuilder.create()
|
||||
fun loadSelectedModes(): List<String> {
|
||||
return satDataRepository.loadSelectedModes()
|
||||
}
|
||||
|
||||
fun saveSelectedModes(modes: List<String>) {
|
||||
transModes.value = modes
|
||||
satDataRepository.saveSelectedModes(modes)
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
|
|
|
@ -49,21 +49,19 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
@Inject
|
||||
lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
private val viewModelSat: SatMapViewModel by viewModels()
|
||||
private val viewModel: SatMapViewModel by viewModels()
|
||||
private val minLat = MapView.getTileSystem().minLatitude
|
||||
private val maxLat = MapView.getTileSystem().maxLatitude
|
||||
private val trackPaint = Paint().apply {
|
||||
private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
strokeWidth = 2f
|
||||
style = Paint.Style.STROKE
|
||||
color = Color.RED
|
||||
strokeCap = Paint.Cap.ROUND
|
||||
strokeJoin = Paint.Join.ROUND
|
||||
isAntiAlias = true
|
||||
}
|
||||
private val footprintPaint = Paint().apply {
|
||||
private val footprintPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
color = Color.parseColor("#26FFE082")
|
||||
isAntiAlias = true
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -73,10 +71,9 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
mapView.apply {
|
||||
setMultiTouchControls(true)
|
||||
setTileSource(TileSourceFactory.WIKIMEDIA)
|
||||
val minZoom = getMinZoom(resources.displayMetrics.heightPixels)
|
||||
minZoomLevel = minZoom
|
||||
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels)
|
||||
maxZoomLevel = 6.0
|
||||
controller.setZoom(minZoom + 0.5)
|
||||
controller.setZoom(minZoomLevel + 0.5)
|
||||
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
|
||||
overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT
|
||||
overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT
|
||||
|
@ -86,17 +83,17 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
overlays.addAll(Array(4) { FolderOverlay() })
|
||||
}
|
||||
}
|
||||
binding.fabPrev.setOnClickListener { viewModelSat.scrollSelection(true) }
|
||||
binding.fabNext.setOnClickListener { viewModelSat.scrollSelection(false) }
|
||||
binding.fabPrev.setOnClickListener { viewModel.scrollSelection(true) }
|
||||
binding.fabNext.setOnClickListener { viewModel.scrollSelection(false) }
|
||||
setupObservers(binding)
|
||||
}
|
||||
|
||||
private fun setupObservers(binding: FragmentMapBinding) {
|
||||
viewModelSat.stationPos.observe(viewLifecycleOwner, { renderStationPos(it, binding) })
|
||||
viewModelSat.allSatPositions.observe(viewLifecycleOwner, { renderSatPositions(it, binding) })
|
||||
viewModelSat.satTrack.observe(viewLifecycleOwner, { renderSatTrack(it, binding) })
|
||||
viewModelSat.satFootprint.observe(viewLifecycleOwner, { renderSatFootprint(it, binding) })
|
||||
viewModelSat.satData.observe(viewLifecycleOwner, { renderSatData(it, binding) })
|
||||
viewModel.stationPos.observe(viewLifecycleOwner, { renderStationPos(it, binding) })
|
||||
viewModel.satPositions.observe(viewLifecycleOwner, { renderSatPositions(it, binding) })
|
||||
viewModel.satTrack.observe(viewLifecycleOwner, { renderSatTrack(it, binding) })
|
||||
viewModel.satFootprint.observe(viewLifecycleOwner, { renderSatFootprint(it, binding) })
|
||||
viewModel.satData.observe(viewLifecycleOwner, { renderSatData(it, binding) })
|
||||
}
|
||||
|
||||
private fun renderStationPos(stationPos: Position, binding: FragmentMapBinding) {
|
||||
|
@ -116,7 +113,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
binding.apply {
|
||||
val markers = FolderOverlay()
|
||||
posMap.entries.forEach {
|
||||
if (viewModelSat.shouldUseTextLabels()) {
|
||||
if (viewModel.shouldUseTextLabels()) {
|
||||
Marker(mapView).apply {
|
||||
setInfoWindow(null)
|
||||
textLabelFontSize = 24
|
||||
|
@ -131,7 +128,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
Timber.d(exception)
|
||||
}
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
viewModelSat.selectSatellite(it.key)
|
||||
viewModel.selectSatellite(it.key)
|
||||
return@setOnMarkerClickListener true
|
||||
}
|
||||
markers.add(this)
|
||||
|
@ -147,7 +144,7 @@ class SatMapFragment : Fragment(R.layout.fragment_map) {
|
|||
Timber.d(exception)
|
||||
}
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
viewModelSat.selectSatellite(it.key)
|
||||
viewModel.selectSatellite(it.key)
|
||||
return@setOnMarkerClickListener true
|
||||
}
|
||||
markers.add(this)
|
||||
|
|
|
@ -60,8 +60,8 @@ class SatMapViewModel @Inject constructor(
|
|||
private val _satData = MutableLiveData<SatData>()
|
||||
val satData: LiveData<SatData> = this._satData
|
||||
|
||||
private val _allSatPositions = MutableLiveData<Map<Satellite, Position>>()
|
||||
val allSatPositions: LiveData<Map<Satellite, Position>> = _allSatPositions
|
||||
private val _satPositions = MutableLiveData<Map<Satellite, Position>>()
|
||||
val satPositions: LiveData<Map<Satellite, Position>> = _satPositions
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
|
@ -118,7 +118,7 @@ class SatMapViewModel @Inject constructor(
|
|||
val osmLon = clipLon(Math.toDegrees(satPos.longitude))
|
||||
satPositions[satellite] = Position(osmLat, osmLon)
|
||||
}
|
||||
_allSatPositions.postValue(satPositions)
|
||||
_satPositions.postValue(satPositions)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
<string name="pref_gsp_title">Местоположение наземной станции</string>
|
||||
<string name="pref_pos_gps">Установить позицию по данным GPS</string>
|
||||
<string name="pref_pos_gps_null">Местоположение отключено</string>
|
||||
<string name="pref_pos_gps_null">Нет данных геолокации</string>
|
||||
<string name="pref_pos_gps_error">Нет разрешения использовать геоданные</string>
|
||||
<string name="pref_pos_qth">Установить позицию по локатору QTH</string>
|
||||
<string name="pref_pos_qth_error">Неправильный локатор QTH</string>
|
||||
|
@ -72,11 +72,11 @@
|
|||
\n— Вам за использование этого приложения! \nЖелаю всегда чистого неба над головой!
|
||||
\n— <a href="https://github.com/g4dpz">David A. B. Johnson</a> и
|
||||
<a href="https://github.com/davidmoten">Dave Moten</a> за работу над библиотекой predict4java и
|
||||
открытие доступа к ней по лицензии <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>
|
||||
за доступ к ней по лицензии <a href="https://gnu.org/licenses/old-licenses/gpl-2.0.en.html">GNU GPLv2</a>
|
||||
\n— <a href="https://github.com/csete">Alexandru Csete</a> за его замечательную
|
||||
программу Gpredict, которая вдохновила меня на создание Look4Sat.
|
||||
\n— <a href="https://celestrak.com/webmaster.php">Доктору T.S. Kelso</a> за его сайт
|
||||
<a href="https://celestrak.com/">Celestrak</a>, который предоставляет доступ к файлам орбит Two-Line Element.
|
||||
<a href="https://celestrak.com/">Celestrak</a> и доступ к файлам орбит Two-Line Element.
|
||||
\n— <a href="https://libre.space/">Libre Space Foundation</a> за проект
|
||||
<a href="https://db.satnogs.org/">SatNOGS</a>, API и базу данных с огромным количеством информации о спутниках.
|
||||
\n— <a href="https://square.github.io/">Square</a> за библиотеки OkHttp, Retrofit, Moshi и доступ
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">Look4Sat</string>
|
||||
<string name="placeholder" translatable="false">Placeholder</string>
|
||||
<string name="placeholder" translatable="false">dummy</string>
|
||||
|
||||
<string name="no">No</string>
|
||||
<string name="yes">Yes</string>
|
||||
|
@ -60,7 +60,7 @@
|
|||
|
||||
<string name="pref_gsp_title">Ground station position</string>
|
||||
<string name="pref_pos_gps">Set position from GPS data</string>
|
||||
<string name="pref_pos_gps_null">Location is switched off</string>
|
||||
<string name="pref_pos_gps_null">No geolocation data</string>
|
||||
<string name="pref_pos_gps_error">Check your location permissions</string>
|
||||
<string name="pref_pos_qth">Set position from QTH locator</string>
|
||||
<string name="pref_pos_qth_error">Invalid QTH locator</string>
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
android:title="@string/pref_map_labels_title"
|
||||
app:iconSpaceReserved="false">
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:defaultValue="true"
|
||||
android:key="shouldUseTextLabels"
|
||||
android:title="@string/pref_map_labels"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
|
|
@ -42,23 +42,11 @@ class SatDataRepository(
|
|||
"https://www.prismnet.com/~mmccants/tles/inttles.zip"
|
||||
)
|
||||
|
||||
private val transmittersModes = arrayOf(
|
||||
"AFSK", "AFSK S-Net", "AFSK SALSAT", "AHRPT", "AM", "APT", "BPSK", "BPSK PMT-A3",
|
||||
"CERTO", "CW", "DQPSK", "DSTAR", "DUV", "FFSK", "FM", "FMN", "FSK", "FSK AX.100 Mode 5",
|
||||
"FSK AX.100 Mode 6", "FSK AX.25 G3RUH", "GFSK", "GFSK Rktr", "GMSK", "HRPT", "LoRa",
|
||||
"LRPT", "LSB", "MFSK", "MSK", "MSK AX.100 Mode 5", "MSK AX.100 Mode 6", "OFDM", "OQPSK",
|
||||
"PSK", "PSK31", "PSK63", "QPSK", "QPSK31", "QPSK63", "SSTV", "USB", "WSJT"
|
||||
)
|
||||
|
||||
fun getAllModes(): Array<String> {
|
||||
return transmittersModes
|
||||
}
|
||||
|
||||
fun saveModesSelection(modes: List<String>) {
|
||||
fun saveSelectedModes(modes: List<String>) {
|
||||
preferencesSource.saveModesSelection(modes)
|
||||
}
|
||||
|
||||
fun loadModesSelection(): List<String> {
|
||||
fun loadSelectedModes(): List<String> {
|
||||
return preferencesSource.loadModesSelection()
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Added simple custom source TLE update dialog
|
||||
Added immediate initial setup
|
||||
Added pass direction arrow to radar view #52
|
||||
Added orientation pointer to radar view
|
||||
Fixed radar view update bug #51
|
||||
Switched to modular architecture
|
Przed Szerokość: | Wysokość: | Rozmiar: 184 KiB Po Szerokość: | Wysokość: | Rozmiar: 299 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 430 KiB Po Szerokość: | Wysokość: | Rozmiar: 592 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 259 KiB Po Szerokość: | Wysokość: | Rozmiar: 302 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 794 KiB Po Szerokość: | Wysokość: | Rozmiar: 795 KiB |
|
@ -1,5 +1,6 @@
|
|||
Removed hardcoded screen orientation
|
||||
Removed redundant permission from manifest
|
||||
Added antenna rotator control via socket
|
||||
Showing azimuth for respective maxElevation
|
||||
Showing satellite name on the polar screen
|
||||
Added simple custom source TLE update dialog
|
||||
Added immediate initial setup
|
||||
Added pass direction arrow to radar view #52
|
||||
Added orientation pointer to radar view
|
||||
Fixed radar view update bug #51
|
||||
Switched to modular architecture
|