Added map in compose, removed MapFragment

This commit is contained in:
Arty Bishop committed 2025-12-27 15:01:32 +00:00
1 parent acb851d4f6
commit c592b8d3ce
23 files changed
+333 -737

No files matched your search

-1
View File
@@ -3,7 +3,6 @@ plugins {
id "org.jetbrains.kotlin.android" id "org.jetbrains.kotlin.android"
id "org.jetbrains.kotlin.kapt" id "org.jetbrains.kotlin.kapt"
id "dagger.hilt.android.plugin" id "dagger.hilt.android.plugin"
id "androidx.navigation.safeargs.kotlin"
} }
android { android {
@@ -26,12 +26,7 @@ import android.os.SystemClock
import android.view.View import android.view.View
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.annotation.IdRes
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.navigation.fragment.findNavController
import com.rtbishop.look4sat.presentation.bottomNav.MainScreenView import com.rtbishop.look4sat.presentation.bottomNav.MainScreenView
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -54,24 +49,6 @@ class MainActivity : ComponentActivity() {
} }
} }
fun <T> Fragment.getNavResult(@IdRes id: Int, key: String, onResult: (result: T) -> Unit) {
val backStackEntry = findNavController().getBackStackEntry(id)
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME && backStackEntry.savedStateHandle.contains(key)) {
backStackEntry.savedStateHandle.get<T>(key)?.let(onResult)
backStackEntry.savedStateHandle.remove<T>(key)
}
}
backStackEntry.lifecycle.addObserver(observer)
viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) backStackEntry.lifecycle.removeObserver(observer)
})
}
fun <T> Fragment.setNavResult(key: String, value: T) {
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, value)
}
fun View.clickWithDebounce(debounceTime: Long = 875L, action: () -> Unit) { fun View.clickWithDebounce(debounceTime: Long = 875L, action: () -> Unit) {
this.setOnClickListener(object : View.OnClickListener { this.setOnClickListener(object : View.OnClickListener {
private var lastClickTime: Long = 0 private var lastClickTime: Long = 0
@@ -20,6 +20,7 @@ import androidx.navigation.compose.rememberNavController
import com.rtbishop.look4sat.presentation.MainTheme import com.rtbishop.look4sat.presentation.MainTheme
import com.rtbishop.look4sat.presentation.aboutScreen.AboutScreen import com.rtbishop.look4sat.presentation.aboutScreen.AboutScreen
import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreen import com.rtbishop.look4sat.presentation.entriesScreen.EntriesScreen
import com.rtbishop.look4sat.presentation.mapScreen.MapScreen
import com.rtbishop.look4sat.presentation.passesScreen.PassesScreen import com.rtbishop.look4sat.presentation.passesScreen.PassesScreen
import com.rtbishop.look4sat.presentation.passesScreen.PassesViewModel import com.rtbishop.look4sat.presentation.passesScreen.PassesViewModel
@@ -58,7 +59,7 @@ fun NavigationGraph(navController: NavHostController) {
EntriesScreen(navToPasses, passesViewModel = passesViewModel) EntriesScreen(navToPasses, passesViewModel = passesViewModel)
} }
composable(BottomNavItem.Passes.screen_route) { PassesScreen(viewModel = passesViewModel) } composable(BottomNavItem.Passes.screen_route) { PassesScreen(viewModel = passesViewModel) }
composable(BottomNavItem.WorldMap.screen_route) {} composable(BottomNavItem.WorldMap.screen_route) { MapScreen() }
composable(BottomNavItem.Settings.screen_route) {} composable(BottomNavItem.Settings.screen_route) {}
composable(BottomNavItem.About.screen_route) { AboutScreen() } composable(BottomNavItem.About.screen_route) { AboutScreen() }
} }
@@ -28,7 +28,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogModesBinding import com.rtbishop.look4sat.databinding.DialogModesBinding
import com.rtbishop.look4sat.domain.ISettingsManager import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@@ -74,7 +73,7 @@ class ModesDialog : AppCompatDialogFragment(), ModesAdapter.ModesClickListener {
} }
modesBtnNeg.setOnClickListener { dismiss() } modesBtnNeg.setOnClickListener { dismiss() }
modesBtnPos.setOnClickListener { modesBtnPos.setOnClickListener {
setNavResult("modes", selectedModes.toList()) // setNavResult("modes", selectedModes.toList())
dismiss() dismiss()
} }
} }
@@ -1,240 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.rtbishop.look4sat.presentation.mapScreen
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.predict.Satellite
import com.rtbishop.look4sat.presentation.clickWithDebounce
import dagger.hilt.android.AndroidEntryPoint
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.FolderOverlay
import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.Polyline
@AndroidEntryPoint
class MapFragment : Fragment(R.layout.fragment_map) {
private val viewModel: MapViewModel by viewModels()
private val navArgs: MapFragmentArgs by navArgs()
private val minLat = MapView.getTileSystem().minLatitude
private val maxLat = MapView.getTileSystem().maxLatitude
private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
strokeWidth = 3f
style = Paint.Style.STROKE
color = Color.RED
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
}
private val footprintPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
strokeWidth = 3f
style = Paint.Style.FILL_AND_STROKE
color = Color.parseColor("#FFE082")
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
textSize = 36f
style = Paint.Style.FILL
color = Color.parseColor("#CCFFFFFF")
setShadowLayer(3f, 3f, 3f, Color.BLACK)
}
private val labelRect = Rect()
private lateinit var binding: FragmentMapBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentMapBinding.bind(view)
setupComponents()
setupObservers()
}
private fun setupComponents() {
val context = requireContext()
val preferences = context.getSharedPreferences("default", Context.MODE_PRIVATE)
Configuration.getInstance().load(context, preferences)
binding.run {
mapView.apply {
setMultiTouchControls(true)
setTileSource(TileSourceFactory.WIKIMEDIA)
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25
maxZoomLevel = 5.75
controller.setZoom(minZoomLevel + 0.25)
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT
overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT
overlayManager.tilesOverlay.setColorFilter(getColorFilter())
setScrollableAreaLimitLatitude(maxLat, minLat, 0)
// add overlays: 0 - GSP, 1 - SatTrack, 2 - SatFootprint, 3 - SatIcons
overlays.addAll(Array(4) { FolderOverlay() })
}
mapBtnBack.clickWithDebounce { findNavController().navigateUp() }
mapBtnPrev.setOnClickListener { viewModel.scrollSelection(true) }
mapBtnNext.setOnClickListener { viewModel.scrollSelection(false) }
}
}
private fun setupObservers() {
viewModel.selectDefaultSatellite(navArgs.catNum)
viewModel.stationPos.observe(viewLifecycleOwner) { handleStationPos(it) }
viewModel.positions.observe(viewLifecycleOwner) { handlePositions(it) }
viewModel.track.observe(viewLifecycleOwner) { handleTrack(it) }
viewModel.footprint.observe(viewLifecycleOwner) { handleFootprint(it) }
viewModel.mapData.observe(viewLifecycleOwner) { handleMapData(it) }
}
private fun handleStationPos(stationPos: GeoPos) {
binding.apply {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_position)
position = GeoPoint(stationPos.lat, stationPos.lon)
mapView.overlays[0] = this
mapView.invalidate()
}
}
}
private fun handlePositions(posMap: Map<Satellite, GeoPos>) {
binding.apply {
val markers = FolderOverlay()
posMap.entries.forEach {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
icon = getCustomTextIcon(it.key.data.name)
try {
position = GeoPoint(it.value.lat, it.value.lon)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
setOnMarkerClickListener { _, _ ->
viewModel.selectSatellite(it.key)
return@setOnMarkerClickListener true
}
markers.add(this)
}
}
mapView.overlays[3] = markers
mapView.invalidate()
}
}
private fun getCustomTextIcon(textLabel: String): Drawable {
textPaint.getTextBounds(textLabel, 0, textLabel.length, labelRect)
val iconSize = 8f
val width = labelRect.width() + iconSize * 2f
val height = textPaint.textSize * 3f + iconSize * 2f
val bitmap = Bitmap.createBitmap(width.toInt(), height.toInt(), Bitmap.Config.ARGB_8888)
Canvas(bitmap).run {
drawCircle(width / 2f, height / 2f, iconSize, textPaint)
drawText(textLabel, iconSize / 2f, height - iconSize, textPaint)
}
return BitmapDrawable(binding.mapView.context.resources, bitmap)
}
private fun handleTrack(satTrack: List<List<GeoPos>>) {
val center = GeoPoint(satTrack[0][0].lat, satTrack[0][0].lon)
val trackOverlay = FolderOverlay()
satTrack.forEach { track ->
val trackPoints = track.map { GeoPoint(it.lat, it.lon) }
Polyline().apply {
try {
setPoints(trackPoints)
outlinePaint.set(trackPaint)
trackOverlay.add(this)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
}
}
binding.mapView.overlays[1] = trackOverlay
binding.mapView.controller.animateTo(center)
}
private fun handleFootprint(satPos: SatPos) {
val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.lat, it.lon) }
val footprintOverlay = Polyline().apply {
outlinePaint.set(footprintPaint)
try {
setPoints(footprintPoints)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
}
binding.mapView.overlays[2] = footprintOverlay
}
private fun handleMapData(mapData: MapData) {
binding.apply {
mapTimer.text = mapData.aosTime
mapDataPeriod.text = String.format(getString(R.string.map_period, mapData.period))
mapDataPhase.text = String.format(getString(R.string.map_phase), mapData.phase)
mapAzimuth.text = String.format(getString(R.string.map_azimuth), mapData.azimuth)
mapElevation.text = String.format(getString(R.string.map_elevation), mapData.elevation)
mapDataAlt.text = String.format(getString(R.string.map_altitude), mapData.altitude)
mapDataDst.text = String.format(getString(R.string.map_distance), mapData.range)
mapDataLat.text =
String.format(getString(R.string.map_latitude), mapData.osmPos.lat)
mapDataLon.text =
String.format(getString(R.string.map_longitude), mapData.osmPos.lon)
mapDataQth.text = String.format(getString(R.string.map_qth), mapData.qthLoc)
mapDataVel.text = String.format(getString(R.string.map_velocity), mapData.velocity)
if (mapData.eclipsed) {
mapDataVisibility.text = getString(R.string.map_eclipsed)
} else {
mapDataVisibility.text = getString(R.string.map_visible)
}
}
binding.mapView.invalidate()
}
private fun getColorFilter(): ColorMatrixColorFilter {
val grayScaleMatrix = ColorMatrix().apply { setSaturation(0f) }
val negativeMatrix = ColorMatrix(
floatArrayOf(
-1f, 0f, 0f, 0f, 255f,
0f, -1f, 0f, 0f, 255f,
0f, 0f, -1f, 0f, 255f,
0f, 0f, 0f, 1f, 0f
)
)
negativeMatrix.preConcat(grayScaleMatrix)
return ColorMatrixColorFilter(negativeMatrix)
}
private fun getMinZoom(screenHeight: Int): Double {
return MapView.getTileSystem().getLatitudeZoom(maxLat, minLat, screenHeight)
}
}
@@ -0,0 +1,247 @@
package com.rtbishop.look4sat.presentation.mapScreen
import android.content.Context
import android.graphics.*
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ElevatedCard
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
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 com.rtbishop.look4sat.R
import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.domain.predict.Satellite
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.FolderOverlay
import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.Polyline
import timber.log.Timber
private val minLat = MapView.getTileSystem().minLatitude
private val maxLat = MapView.getTileSystem().maxLatitude
private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
strokeWidth = 3f
style = Paint.Style.STROKE
color = Color.RED
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
}
private val footprintPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
strokeWidth = 3f
style = Paint.Style.FILL_AND_STROKE
color = Color.parseColor("#FFE082")
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
textSize = 36f
style = Paint.Style.FILL
color = Color.parseColor("#CCFFFFFF")
setShadowLayer(3f, 3f, 3f, Color.BLACK)
}
private val labelRect = Rect()
@Composable
fun MapScreen(viewModel: MapViewModel = hiltViewModel()) {
val context = LocalContext.current
Configuration.getInstance().load(context, viewModel.getPreferences())
viewModel.selectDefaultSatellite(-1)
val stationPos = viewModel.stationPos.observeAsState()
val positions = viewModel.positions.observeAsState()
val satTrack = viewModel.track.observeAsState()
val footprint = viewModel.footprint.observeAsState()
val positionClick = { satellite: Satellite -> viewModel.selectSatellite(satellite) }
Timber.d("MapScreen recomposition")
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
ElevatedCard(modifier = Modifier.fillMaxSize()) {
MapView(modifier = Modifier.fillMaxSize()) { mapView ->
Timber.d("MapView recomposition")
stationPos.value?.let { setStationPosition(it, mapView) }
positions.value?.let { setPositions(it, mapView, positionClick) }
satTrack.value?.let { setSatelliteTrack(it, mapView) }
footprint.value?.let { setFootprint(it, mapView) }
}
}
}
}
private fun setStationPosition(stationPos: GeoPos, mapView: MapView) {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
icon = ContextCompat.getDrawable(mapView.context, R.drawable.ic_position)
position = GeoPoint(stationPos.lat, stationPos.lon)
mapView.overlays[0] = this
mapView.invalidate()
}
}
private fun setPositions(
posMap: Map<Satellite, GeoPos>, mapView: MapView, action: (Satellite) -> Unit
) {
val markers = FolderOverlay()
posMap.entries.forEach {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
icon = getCustomTextIcon(it.key.data.name, mapView)
try {
position = GeoPoint(it.value.lat, it.value.lon)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
setOnMarkerClickListener { _, _ ->
action(it.key)
return@setOnMarkerClickListener true
}
markers.add(this)
}
}
mapView.overlays[3] = markers
mapView.invalidate()
}
private fun getCustomTextIcon(textLabel: String, mapView: MapView): Drawable {
textPaint.getTextBounds(textLabel, 0, textLabel.length, labelRect)
val iconSize = 8f
val width = labelRect.width() + iconSize * 2f
val height = textPaint.textSize * 3f + iconSize * 2f
val bitmap = Bitmap.createBitmap(width.toInt(), height.toInt(), Bitmap.Config.ARGB_8888)
Canvas(bitmap).run {
drawCircle(width / 2f, height / 2f, iconSize, textPaint)
drawText(textLabel, iconSize / 2f, height - iconSize, textPaint)
}
return BitmapDrawable(mapView.context.resources, bitmap)
}
private fun setSatelliteTrack(satTrack: List<List<GeoPos>>, mapView: MapView) {
// val center = GeoPoint(satTrack[0][0].lat, satTrack[0][0].lon)
val trackOverlay = FolderOverlay()
satTrack.forEach { track ->
val trackPoints = track.map { GeoPoint(it.lat, it.lon) }
Polyline().apply {
try {
setPoints(trackPoints)
outlinePaint.set(trackPaint)
trackOverlay.add(this)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
}
}
mapView.overlays[1] = trackOverlay
// mapView.controller.animateTo(center)
}
private fun setFootprint(satPos: SatPos, mapView: MapView) {
val footprintPoints = satPos.getRangeCircle().map { GeoPoint(it.lat, it.lon) }
val footprintOverlay = Polyline().apply {
outlinePaint.set(footprintPaint)
try {
setPoints(footprintPoints)
} catch (exception: IllegalArgumentException) {
println(exception.stackTraceToString())
}
}
mapView.overlays[2] = footprintOverlay
}
//private fun handleMapData(mapData: MapData) {
// binding.apply {
// mapTimer.text = mapData.aosTime
// mapDataPeriod.text = String.format(getString(R.string.map_period, mapData.period))
// mapDataPhase.text = String.format(getString(R.string.map_phase), mapData.phase)
// mapAzimuth.text = String.format(getString(R.string.map_azimuth), mapData.azimuth)
// mapElevation.text = String.format(getString(R.string.map_elevation), mapData.elevation)
// mapDataAlt.text = String.format(getString(R.string.map_altitude), mapData.altitude)
// mapDataDst.text = String.format(getString(R.string.map_distance), mapData.range)
// mapDataLat.text =
// String.format(getString(R.string.map_latitude), mapData.osmPos.lat)
// mapDataLon.text =
// String.format(getString(R.string.map_longitude), mapData.osmPos.lon)
// mapDataQth.text = String.format(getString(R.string.map_qth), mapData.qthLoc)
// mapDataVel.text = String.format(getString(R.string.map_velocity), mapData.velocity)
// if (mapData.eclipsed) {
// mapDataVisibility.text = getString(R.string.map_eclipsed)
// } else {
// mapDataVisibility.text = getString(R.string.map_visible)
// }
// }
// binding.mapView.invalidate()
//}
@Composable
private fun MapView(modifier: Modifier = Modifier, update: ((map: MapView) -> Unit)? = null) {
val mapView = rememberMapViewWithLifecycle()
AndroidView({ mapView }, modifier) { update?.invoke(it) }
}
@Composable
private fun rememberMapViewWithLifecycle(context: Context = LocalContext.current): MapView {
// add clipToOutline = true if needed
val mapView = remember { MapView(context).apply { id = R.id.osm_map_view } }.apply {
setMultiTouchControls(true)
setTileSource(TileSourceFactory.WIKIMEDIA)
minZoomLevel = getMinZoom(resources.displayMetrics.heightPixels) + 0.25
maxZoomLevel = 5.75
controller.setZoom(minZoomLevel + 0.25)
zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT
overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT
overlayManager.tilesOverlay.setColorFilter(getColorFilter())
setScrollableAreaLimitLatitude(maxLat, minLat, 0)
// add overlays: 0 - GSP, 1 - SatTrack, 2 - SatFootprint, 3 - SatIcons
overlays.addAll(Array(4) { FolderOverlay() })
}
// Makes MapView follow the lifecycle of this composable
val lifecycleObserver = rememberMapViewLifecycleObserver(mapView)
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
lifecycle.addObserver(lifecycleObserver)
onDispose { lifecycle.removeObserver(lifecycleObserver) }
}
return mapView
}
@Composable
private fun rememberMapViewLifecycleObserver(mapView: MapView) = remember(mapView) {
LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> mapView.onResume()
Lifecycle.Event.ON_PAUSE -> mapView.onPause()
else -> {}
}
}
}
private fun getColorFilter(): ColorMatrixColorFilter {
val grayScaleMatrix = ColorMatrix().apply { setSaturation(0f) }
val negativeMatrix = ColorMatrix(
floatArrayOf(
-1f, 0f, 0f, 0f, 255f, 0f, -1f, 0f, 0f, 255f, 0f, 0f, -1f, 0f, 255f, 0f, 0f, 0f, 1f, 0f
)
)
negativeMatrix.preConcat(grayScaleMatrix)
return ColorMatrixColorFilter(negativeMatrix)
}
private fun getMinZoom(screenHeight: Int): Double {
return MapView.getTileSystem().getLatitudeZoom(maxLat, minLat, screenHeight)
}
@@ -17,6 +17,7 @@
*/ */
package com.rtbishop.look4sat.presentation.mapScreen package com.rtbishop.look4sat.presentation.mapScreen
import android.content.SharedPreferences
import androidx.lifecycle.* import androidx.lifecycle.*
import com.rtbishop.look4sat.domain.IDataRepository import com.rtbishop.look4sat.domain.IDataRepository
import com.rtbishop.look4sat.domain.ISatelliteManager import com.rtbishop.look4sat.domain.ISatelliteManager
@@ -36,9 +37,10 @@ import kotlin.math.min
@HiltViewModel @HiltViewModel
class MapViewModel @Inject constructor( class MapViewModel @Inject constructor(
private val satelliteManager: ISatelliteManager, private val preferences: SharedPreferences,
private val repository: IDataRepository, private val repository: IDataRepository,
private val settings: ISettingsManager, private val satelliteManager: ISatelliteManager,
private val settings: ISettingsManager
) : ViewModel() { ) : ViewModel() {
private val stationPosition = settings.loadStationPosition() private val stationPosition = settings.loadStationPosition()
@@ -66,6 +68,8 @@ class MapViewModel @Inject constructor(
private val _positions = MutableLiveData<Map<Satellite, GeoPos>>() private val _positions = MutableLiveData<Map<Satellite, GeoPos>>()
val positions: LiveData<Map<Satellite, GeoPos>> = _positions val positions: LiveData<Map<Satellite, GeoPos>> = _positions
fun getPreferences() = preferences
fun scrollSelection(decrement: Boolean) { fun scrollSelection(decrement: Boolean) {
if (allSatellites.isNotEmpty()) { if (allSatellites.isNotEmpty()) {
val index = allSatellites.indexOf(selectedSatellite) val index = allSatellites.indexOf(selectedSatellite)
@@ -184,8 +188,19 @@ class MapViewModel @Inject constructor(
val phase = satPos.phase.toDegrees() val phase = satPos.phase.toDegrees()
val visibility = satPos.eclipsed val visibility = satPos.eclipsed
val satData = MapData( val satData = MapData(
sat.data.catnum, sat.data.name, aosTime, azimuth, elevation, satPos.distance, sat.data.catnum,
satPos.altitude, velocity, qthLoc, osmPos, sat.data.orbitalPeriod, phase, visibility sat.data.name,
aosTime,
azimuth,
elevation,
satPos.distance,
satPos.altitude,
velocity,
qthLoc,
osmPos,
sat.data.orbitalPeriod,
phase,
visibility
) )
_mapData.postValue(satData) _mapData.postValue(satData)
} }
@@ -17,14 +17,10 @@
*/ */
package com.rtbishop.look4sat.presentation.radarScreen package com.rtbishop.look4sat.presentation.radarScreen
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.provider.CalendarContract
import android.view.View import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
@@ -32,7 +28,6 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentRadarBinding import com.rtbishop.look4sat.databinding.FragmentRadarBinding
import com.rtbishop.look4sat.domain.predict.SatPass import com.rtbishop.look4sat.domain.predict.SatPass
import com.rtbishop.look4sat.domain.predict.SatPos import com.rtbishop.look4sat.domain.predict.SatPos
import com.rtbishop.look4sat.presentation.clickWithDebounce
import com.rtbishop.look4sat.utility.toDegrees import com.rtbishop.look4sat.utility.toDegrees
import com.rtbishop.look4sat.utility.toTimerString import com.rtbishop.look4sat.utility.toTimerString
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -41,7 +36,6 @@ import dagger.hilt.android.AndroidEntryPoint
class RadarFragment : Fragment(R.layout.fragment_radar) { class RadarFragment : Fragment(R.layout.fragment_radar) {
private val viewModel: RadarViewModel by viewModels() private val viewModel: RadarViewModel by viewModels()
private val navArgs: RadarFragmentArgs by navArgs()
private val radioAdapter = RadioAdapter() private val radioAdapter = RadioAdapter()
private var binding: FragmentRadarBinding? = null private var binding: FragmentRadarBinding? = null
private var radarView: RadarView? = null private var radarView: RadarView? = null
@@ -78,7 +72,7 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
} }
private fun setupObservers() { private fun setupObservers() {
viewModel.getPass(navArgs.catNum, navArgs.aosTime).observe(viewLifecycleOwner) { pass -> viewModel.getPass(45000, System.currentTimeMillis()).observe(viewLifecycleOwner) { pass ->
binding?.run { binding?.run {
radarView = RadarView(requireContext()).apply { radarView = RadarView(requireContext()).apply {
setShowAim(viewModel.getUseCompass()) setShowAim(viewModel.getUseCompass())
@@ -103,24 +97,16 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
viewModel.orientation.observe(viewLifecycleOwner) { value -> viewModel.orientation.observe(viewLifecycleOwner) { value ->
radarView?.setOrientation(value.first, value.second, value.third) radarView?.setOrientation(value.first, value.second, value.third)
} }
radarBtnBack.clickWithDebounce { findNavController().navigateUp() } // radarBtnBack.clickWithDebounce { findNavController().navigateUp() }
radarBtnMap.clickWithDebounce { // radarBtnMap.clickWithDebounce {
val direction = RadarFragmentDirections.globalToMap(pass.catNum) // val direction = RadarFragmentDirections.globalToMap(pass.catNum)
findNavController().navigate(direction) // findNavController().navigate(direction)
} // }
radarBtnNotify.clickWithDebounce { // radarBtnNotify.isEnabled = false
val intent = Intent(Intent.ACTION_INSERT) // radarBtnSettings.clickWithDebounce {
.setData(CalendarContract.Events.CONTENT_URI) // val direction = RadarFragmentDirections.globalToSettings()
.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, pass.aosTime) // findNavController().navigate(direction)
.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, pass.losTime) // }
.putExtra(CalendarContract.Events.TITLE, pass.name)
.putExtra(CalendarContract.Events.DESCRIPTION, "Look4Sat")
startActivity(intent)
}
radarBtnSettings.clickWithDebounce {
val direction = RadarFragmentDirections.globalToSettings()
findNavController().navigate(direction)
}
} }
} }
} }
@@ -150,7 +136,7 @@ class RadarFragment : Fragment(R.layout.fragment_radar) {
radarTimer.text = millisBeforeEnd.toTimerString() radarTimer.text = millisBeforeEnd.toTimerString()
if (timeNow > satPass.losTime) { if (timeNow > satPass.losTime) {
radarTimer.text = 0L.toTimerString() radarTimer.text = 0L.toTimerString()
findNavController().navigateUp() // findNavController().navigateUp()
} }
} }
} else radarTimer.text = 0L.toTimerString() } else radarTimer.text = 0L.toTimerString()
@@ -26,7 +26,6 @@ import androidx.appcompat.app.AppCompatDialogFragment
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogLocatorBinding import com.rtbishop.look4sat.databinding.DialogLocatorBinding
import com.rtbishop.look4sat.domain.ISettingsManager import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@@ -50,7 +49,7 @@ class LocatorDialog : AppCompatDialogFragment() {
) )
locatorEdit.setText(preferences.loadStationLocator()) locatorEdit.setText(preferences.loadStationLocator())
locatorBtnPos.setOnClickListener { locatorBtnPos.setOnClickListener {
setNavResult("locator", locatorEdit.text.toString()) // setNavResult("locator", locatorEdit.text.toString())
dismiss() dismiss()
} }
locatorBtnNeg.setOnClickListener { dismiss() } locatorBtnNeg.setOnClickListener { dismiss() }
@@ -26,7 +26,6 @@ import androidx.appcompat.app.AppCompatDialogFragment
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.DialogPositionBinding import com.rtbishop.look4sat.databinding.DialogPositionBinding
import com.rtbishop.look4sat.domain.ISettingsManager import com.rtbishop.look4sat.domain.ISettingsManager
import com.rtbishop.look4sat.presentation.setNavResult
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@@ -52,17 +51,17 @@ class PositionDialog : AppCompatDialogFragment() {
positionLatEdit.setText(location.lat.toString()) positionLatEdit.setText(location.lat.toString())
positionLonEdit.setText(location.lon.toString()) positionLonEdit.setText(location.lon.toString())
positionBtnPos.setOnClickListener { positionBtnPos.setOnClickListener {
val latitude = try { // val latitude = try {
positionLatEdit.text.toString().toDouble() // positionLatEdit.text.toString().toDouble()
} catch (exception: Exception) { // } catch (exception: Exception) {
180.0 // 180.0
} // }
val longitude = try { // val longitude = try {
positionLonEdit.text.toString().toDouble() // positionLonEdit.text.toString().toDouble()
} catch (exception: Exception) { // } catch (exception: Exception) {
400.0 // 400.0
} // }
setNavResult("position", Pair(latitude, longitude)) // setNavResult("position", Pair(latitude, longitude))
dismiss() dismiss()
} }
positionBtnNeg.setOnClickListener { dismiss() } positionBtnNeg.setOnClickListener { dismiss() }
@@ -31,14 +31,12 @@ import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.asLiveData import androidx.lifecycle.asLiveData
import androidx.navigation.fragment.findNavController
import com.rtbishop.look4sat.BuildConfig import com.rtbishop.look4sat.BuildConfig
import com.rtbishop.look4sat.R import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentSettingsBinding import com.rtbishop.look4sat.databinding.FragmentSettingsBinding
import com.rtbishop.look4sat.domain.model.DataState import com.rtbishop.look4sat.domain.model.DataState
import com.rtbishop.look4sat.domain.predict.GeoPos import com.rtbishop.look4sat.domain.predict.GeoPos
import com.rtbishop.look4sat.presentation.clickWithDebounce import com.rtbishop.look4sat.presentation.clickWithDebounce
import com.rtbishop.look4sat.presentation.getNavResult
import com.rtbishop.look4sat.utility.isValidIPv4 import com.rtbishop.look4sat.utility.isValidIPv4
import com.rtbishop.look4sat.utility.isValidPort import com.rtbishop.look4sat.utility.isValidPort
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -79,7 +77,7 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding = FragmentSettingsBinding.bind(view).apply { binding = FragmentSettingsBinding.bind(view).apply {
settingsBtnBack.clickWithDebounce { findNavController().navigateUp() } // settingsBtnBack.clickWithDebounce { findNavController().navigateUp() }
settingsScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY -> settingsScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, y, _, newY ->
if (y > newY) settingsFab.hide() else settingsFab.show() if (y > newY) settingsFab.hide() else settingsFab.show()
}) })
@@ -115,20 +113,20 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
settingsLocation.locationBtnGps.clickWithDebounce { settingsLocation.locationBtnGps.clickWithDebounce {
locationRequest.launch(arrayOf(locationFine, locationCoarse)) locationRequest.launch(arrayOf(locationFine, locationCoarse))
} }
settingsLocation.locationBtnManual.clickWithDebounce { // settingsLocation.locationBtnManual.clickWithDebounce {
val action = SettingsFragmentDirections.globalToPosition() // val action = SettingsFragmentDirections.globalToPosition()
findNavController().navigate(action) // findNavController().navigate(action)
} // }
settingsLocation.locationBtnQth.clickWithDebounce { // settingsLocation.locationBtnQth.clickWithDebounce {
val action = SettingsFragmentDirections.globalToLocator() // val action = SettingsFragmentDirections.globalToLocator()
findNavController().navigate(action) // findNavController().navigate(action)
} // }
getNavResult<Pair<Double, Double>>(R.id.nav_settings, "position") { position -> // getNavResult<Pair<Double, Double>>(R.id.nav_settings, "position") { position ->
viewModel.setStationPosition(position.first, position.second) // viewModel.setStationPosition(position.first, position.second)
} // }
getNavResult<String>(R.id.nav_settings, "locator") { locator -> // getNavResult<String>(R.id.nav_settings, "locator") { locator ->
viewModel.setPositionFromQth(locator) // viewModel.setPositionFromQth(locator)
} // }
} }
} }
@@ -146,9 +144,9 @@ class SettingsFragment : Fragment(R.layout.fragment_settings) {
val radiosFormat = getString(R.string.data_radios) val radiosFormat = getString(R.string.data_radios)
settingsData.dataRadios.text = String.format(radiosFormat, number) settingsData.dataRadios.text = String.format(radiosFormat, number)
} }
getNavResult<List<String>>(R.id.nav_settings, "sources") { // getNavResult<List<String>>(R.id.nav_settings, "sources") {
viewModel.updateFromWeb() // viewModel.updateFromWeb()
} // }
} }
} }
@@ -1,7 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="#000000" android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
+2 -1
View File
@@ -1,7 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="#000000" android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
+10 -3
View File
@@ -1,4 +1,11 @@
<vector android:height="24dp" android:viewportHeight="512" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="512" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
<path android:fillColor="#000000" android:pathData="M495.3,425.74H255.18c-3.58,-52 -23.78,-100.69 -58.12,-140.07L401.37,81.37c6.52,-6.52 6.52,-17.09 0,-23.61c-6.52,-6.52 -17.09,-6.52 -23.61,0L162.23,273.29L4.89,430.63c-4.77,4.77 -6.21,11.96 -3.62,18.2s8.67,10.3 15.42,10.3h222.61h256c9.22,0 16.7,-7.47 16.7,-16.7C512,433.21 504.52,425.74 495.3,425.74z"/> android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M495.3,425.74H255.18c-3.58,-52 -23.78,-100.69 -58.12,-140.07L401.37,81.37c6.52,-6.52 6.52,-17.09 0,-23.61c-6.52,-6.52 -17.09,-6.52 -23.61,0L162.23,273.29L4.89,430.63c-4.77,4.77 -6.21,11.96 -3.62,18.2s8.67,10.3 15.42,10.3h222.61h256c9.22,0 16.7,-7.47 16.7,-16.7C512,433.21 504.52,425.74 495.3,425.74z" />
</vector> </vector>
+2 -1
View File
@@ -1,7 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:tint="#000000" android:autoMirrored="true"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4.25,5.61C6.27,8.2 10,13 10,13v6c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-6c0,0 3.72,-4.8 5.74,-7.39C20.25,4.95 19.78,4 18.95,4H5.04C4.21,4 3.74,4.95 4.25,5.61z" />
</vector>
@@ -1,13 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#000000"
android:pathData="M437.02,74.98C388.67,26.63 324.38,0 256,0S123.33,26.63 74.98,74.98C26.63,123.33 0,187.62 0,256S26.63,388.67 74.98,437.02C123.33,485.37 187.62,512 256,512c68.38,0 132.67,-26.63 181.02,-74.98C485.37,388.67 512,324.38 512,256S485.37,123.33 437.02,74.98zM415.51,415.51c-42.61,42.61 -99.26,66.07 -159.51,66.07c-60.26,0 -116.9,-23.47 -159.51,-66.07C53.88,372.9 30.42,316.26 30.42,256S53.88,139.1 96.49,96.49C139.1,53.88 195.74,30.42 256,30.42c55.16,0 107.29,19.67 148.39,55.68l-41.21,41.21C333.14,102.2 295.63,88.52 256,88.52c-92.35,0 -167.48,75.13 -167.48,167.48S163.65,423.48 256,423.48s167.48,-75.13 167.48,-167.48h-30.42c0,75.57 -61.49,137.06 -137.06,137.06S118.94,331.58 118.94,256S180.43,118.94 256,118.94c31.49,0 61.35,10.56 85.57,29.98l-41.53,41.53c-12.91,-8.72 -28.12,-13.4 -44.04,-13.4c-43.53,0 -78.95,35.42 -78.95,78.95c0,43.53 35.42,78.95 78.95,78.95s78.95,-35.42 78.95,-78.95h-30.42c0,26.76 -21.77,48.54 -48.54,48.54S207.46,282.76 207.46,256c0,-26.76 21.77,-48.54 48.54,-48.54c7.72,0 15.15,1.8 21.84,5.18l-32.6,32.6l21.51,21.51l159.15,-159.15c36.01,41.11 55.68,93.24 55.68,148.39C481.58,316.26 458.12,372.9 415.51,415.51z" />
<path
android:fillColor="#000000"
android:pathData="M396.71,197.71m-18.42,0a18.42,18.42 0,1 1,36.85 0a18.42,18.42 0,1 1,-36.85 0" />
</vector>
@@ -1,6 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal" android:tint="?attr/colorControlNormal"
android:viewportWidth="287.97" android:viewportWidth="287.97"
android:viewportHeight="287.97"> android:viewportHeight="287.97">
@@ -1,22 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="503.61"
android:viewportHeight="503.61">
<path
android:fillColor="#000000"
android:pathData="M251.8,0C113.31,0 0,112.47 0,251.8s113.31,251.8 251.8,251.8s251.8,-113.31 251.8,-251.8S390.3,0 251.8,0zM251.8,486.82c-129.26,0 -235.02,-105.76 -235.02,-235.02S122.54,16.79 251.8,16.79S486.82,122.54 486.82,251.8S381.06,486.82 251.8,486.82z" />
<path
android:fillColor="#000000"
android:pathData="M251.8,83.93c-92.33,0 -167.87,75.54 -167.87,167.87c0,9.23 0.84,18.47 2.52,26.86c0,4.2 5.04,7.55 9.23,6.72c4.2,0 7.55,-5.04 6.72,-9.23c-0.84,-7.55 -1.68,-15.95 -1.68,-24.34c0,-83.1 67.99,-151.08 151.08,-151.08s151.08,67.99 151.08,151.08s-67.99,151.08 -151.08,151.08c-37.55,0 -72.9,-13.13 -100.64,-38.69l47.21,-47.21c15.02,12.31 33.58,18.75 53.43,18.75c46.16,0 83.93,-37.77 83.93,-83.93s-37.77,-83.93 -83.93,-83.93s-83.93,37.77 -83.93,83.93c0,5.04 0,9.23 1.68,13.43c0,4.2 5.04,7.55 9.23,6.72c4.2,0 7.55,-5.04 6.72,-9.23c-0.84,-3.36 -0.84,-7.55 -0.84,-10.91c0,-36.93 30.22,-67.15 67.15,-67.15s67.15,30.22 67.15,67.15c0,36.93 -30.22,67.15 -67.15,67.15c-15.56,0 -29.91,-4.78 -41.58,-13.81l24.48,-24.48c5.02,2.99 10.87,4.72 17.1,4.72c18.47,0 33.57,-15.11 33.57,-33.57s-15.11,-33.57 -33.57,-33.57s-33.57,15.11 -33.57,33.57c0,6.24 1.73,12.09 4.72,17.1l-30.74,30.74c0,0 0,0 0,0s0,0 0,0l-58.75,58.75c0,0 0,0 0,0s0,0 0,0l-30.22,30.22c-3.36,3.36 -3.36,8.39 0,11.75c1.68,1.68 3.36,2.52 5.88,2.52c2.52,0 4.2,-0.84 5.88,-2.52l24.41,-24.41c30.46,28.22 70.69,43.72 112.4,43.72c92.33,0 167.87,-74.7 167.87,-167.87C419.67,159.48 344.13,83.93 251.8,83.93zM251.8,235.02c9.23,0 16.79,7.55 16.79,16.79c0,9.23 -7.55,16.79 -16.79,16.79c-4.18,0 -8.01,-1.56 -10.95,-4.11c-0.24,-0.32 -0.5,-0.63 -0.8,-0.93s-0.61,-0.56 -0.93,-0.8c-2.55,-2.95 -4.11,-6.77 -4.11,-10.95C235.02,242.57 242.57,235.02 251.8,235.02z" />
<path
android:fillColor="#000000"
android:pathData="M92.33,159.48c0,-9.23 -7.55,-16.79 -16.79,-16.79s-16.79,7.55 -16.79,16.79s7.55,16.79 16.79,16.79S92.33,168.71 92.33,159.48z" />
<path
android:fillColor="#000000"
android:pathData="M318.95,327.34c0,9.23 7.55,16.79 16.79,16.79c9.23,0 16.79,-7.55 16.79,-16.79c0,-9.23 -7.55,-16.79 -16.79,-16.79C326.51,310.56 318.95,318.11 318.95,327.34z" />
<path
android:fillColor="#000000"
android:pathData="M360.92,109.11c9.23,0 16.79,-7.55 16.79,-16.79s-7.55,-16.79 -16.79,-16.79s-16.79,7.55 -16.79,16.79S351.68,109.11 360.92,109.11z" />
</vector>
-242
View File
@@ -1,242 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionGroup="true"
android:keepScreenOn="true">
<androidx.cardview.widget.CardView
android:id="@+id/map_toolbar"
style="@style/Toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/map_btn_back"
style="@style/ToolbarButton"
android:layout_gravity="start|center_vertical"
android:contentDescription="@string/btn_back"
android:src="@drawable/ic_back" />
<TextView
android:id="@+id/map_timer"
style="@style/ToolbarTimer" />
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/map_card"
style="@style/SurfaceCard"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/view_default_margin"
android:layout_marginTop="@dimen/view_default_margin"
android:layout_marginEnd="@dimen/view_default_margin"
android:layout_marginBottom="@dimen/view_default_margin"
app:layout_constraintBottom_toTopOf="@+id/map_card_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/map_toolbar">
<org.osmdroid.views.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/map_copyright"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="2dp"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="1"
android:text="@string/map_copyright"
android:textColor="@color/textMain"
android:textSize="@dimen/text_size_copyright"
app:layout_constraintEnd_toEndOf="@+id/map_card"
app:layout_constraintStart_toStartOf="@+id/map_card"
app:layout_constraintTop_toTopOf="@+id/map_card" />
<androidx.cardview.widget.CardView
android:id="@+id/map_card_info"
style="@style/SurfaceCard"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/view_default_margin"
android:layout_marginEnd="@dimen/view_default_margin"
android:layout_marginBottom="@dimen/view_default_margin"
app:layout_constraintBottom_toTopOf="@+id/map_coordinator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/map_azimuth"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/map_azimuth"
app:layout_constraintBottom_toTopOf="@+id/map_data_alt"
app:layout_constraintStart_toStartOf="@+id/map_data_period"
app:layout_constraintTop_toBottomOf="@+id/map_data_period" />
<TextView
android:id="@+id/map_data_lat"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/map_latitude"
app:layout_constraintBottom_toTopOf="@+id/map_data_qth"
app:layout_constraintStart_toStartOf="@+id/map_data_alt"
app:layout_constraintTop_toBottomOf="@+id/map_data_alt" />
<TextView
android:id="@+id/map_data_lon"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/map_longitude"
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_lat"
app:layout_constraintEnd_toEndOf="@+id/map_data_dst" />
<TextView
android:id="@+id/map_data_alt"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/map_altitude"
app:layout_constraintBottom_toTopOf="@+id/map_data_lat"
app:layout_constraintStart_toStartOf="@+id/map_azimuth"
app:layout_constraintTop_toBottomOf="@+id/map_azimuth" />
<TextView
android:id="@+id/map_data_dst"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/map_distance"
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_alt"
app:layout_constraintEnd_toEndOf="@+id/map_elevation" />
<TextView
android:id="@+id/map_elevation"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/map_elevation"
app:layout_constraintBaseline_toBaselineOf="@+id/map_azimuth"
app:layout_constraintEnd_toEndOf="@+id/map_data_phase" />
<TextView
android:id="@+id/map_data_period"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginTop="6dp"
android:text="@string/map_period"
app:layout_constraintBottom_toTopOf="@+id/map_azimuth"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/map_data_phase"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:text="@string/map_phase"
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_period"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/map_data_qth"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="8dp"
android:text="@string/map_qth"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/map_data_lat"
app:layout_constraintTop_toBottomOf="@+id/map_data_lat" />
<TextView
android:id="@+id/map_data_vel"
style="@style/WorldMapText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/map_velocity"
app:layout_constraintBaseline_toBaselineOf="@+id/map_data_qth"
app:layout_constraintEnd_toEndOf="@+id/map_data_lon" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/map_coordinator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/map_bottom_bar"
style="@style/BottomBar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<Button
android:id="@+id/map_btn_prev"
style="@style/NormalButton"
android:layout_width="@dimen/button_width_max"
android:layout_marginStart="@dimen/button_margin_side"
android:text="@string/btn_prev"
app:icon="@drawable/ic_prev"
app:layout_constraintBottom_toBottomOf="@+id/map_coordinator"
app:layout_constraintStart_toStartOf="@+id/map_coordinator"
app:layout_constraintTop_toTopOf="@+id/map_coordinator" />
<TextView
android:id="@+id/map_data_visibility"
style="@style/WorldMapText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:ellipsize="end"
android:gravity="center_horizontal"
android:maxLines="1"
android:text="@string/map_visible"
app:layout_constraintBaseline_toBaselineOf="@+id/map_btn_prev"
app:layout_constraintEnd_toStartOf="@+id/map_btn_next"
app:layout_constraintStart_toEndOf="@+id/map_btn_prev" />
<Button
android:id="@+id/map_btn_next"
style="@style/NormalButton"
android:layout_width="@dimen/button_width_max"
android:layout_marginEnd="@dimen/button_margin_side"
android:text="@string/btn_next"
app:icon="@drawable/ic_next"
app:iconGravity="end"
app:layout_constraintBaseline_toBaselineOf="@+id/map_btn_prev"
app:layout_constraintEnd_toEndOf="@+id/map_coordinator" />
</androidx.constraintlayout.widget.ConstraintLayout>
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<org.osmdroid.views.MapView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/osm_map_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/nav_entries"
android:icon="@drawable/ic_satellite"
android:title="@string/menu_satellites" />
<item
android:id="@+id/nav_passes"
android:icon="@drawable/ic_list"
android:title="@string/menu_passes" />
<item
android:id="@+id/nav_map"
android:icon="@drawable/ic_map"
android:title="@string/menu_map" />
<item
android:id="@+id/nav_settings"
android:icon="@drawable/ic_settings"
android:title="@string/menu_settings" />
</menu>
-94
View File
@@ -1,94 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_root"
app:startDestination="@id/nav_radar">
<fragment
android:id="@+id/nav_radar"
android:name="com.rtbishop.look4sat.presentation.radarScreen.RadarFragment"
tools:layout="@layout/fragment_radar">
<argument
android:name="catNum"
android:defaultValue="-1"
app:argType="integer" />
<argument
android:name="aosTime"
android:defaultValue="0L"
app:argType="long" />
</fragment>
<fragment
android:id="@+id/nav_map"
android:name="com.rtbishop.look4sat.presentation.mapScreen.MapFragment"
tools:layout="@layout/fragment_map">
<argument
android:name="catNum"
android:defaultValue="-1"
app:argType="integer" />
</fragment>
<fragment
android:id="@+id/nav_settings"
android:name="com.rtbishop.look4sat.presentation.settingsScreen.SettingsFragment"
tools:layout="@layout/fragment_settings" />
<dialog
android:id="@+id/nav_modes"
android:name="com.rtbishop.look4sat.presentation.entriesScreen.ModesDialog"
tools:layout="@layout/dialog_modes" />
<dialog
android:id="@+id/nav_position"
android:name="com.rtbishop.look4sat.presentation.settingsScreen.PositionDialog"
tools:layout="@layout/dialog_position" />
<dialog
android:id="@+id/nav_locator"
android:name="com.rtbishop.look4sat.presentation.settingsScreen.LocatorDialog"
tools:layout="@layout/dialog_locator" />
<action
android:id="@+id/global_to_entries"
app:destination="@id/nav_entries"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_passes"
app:destination="@id/nav_passes"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_radar"
app:destination="@id/nav_radar"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_map"
app:destination="@id/nav_map"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_settings"
app:destination="@id/nav_settings"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_position"
app:destination="@id/nav_position"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/global_to_locator"
app:destination="@id/nav_locator"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</navigation>