diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/DataSource.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/DataSource.kt
deleted file mode 100644
index 58a01a49..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/framework/model/DataSource.kt
+++ /dev/null
@@ -1,20 +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 .
- */
-package com.rtbishop.look4sat.framework.model
-
-data class DataSource(var url: String = String())
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
index ec0035c7..707ae20c 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/FilterDialog.kt
@@ -17,54 +17,70 @@
*/
package com.rtbishop.look4sat.presentation.passesScreen
-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 com.rtbishop.look4sat.R
-import com.rtbishop.look4sat.databinding.DialogFilterBinding
-import com.rtbishop.look4sat.domain.ISettingsManager
-import com.rtbishop.look4sat.presentation.setNavResult
-import dagger.hilt.android.AndroidEntryPoint
-import javax.inject.Inject
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.window.Dialog
+import com.rtbishop.look4sat.presentation.CardButton
+import com.rtbishop.look4sat.presentation.MainTheme
-@AndroidEntryPoint
-class FilterDialog : AppCompatDialogFragment() {
+@Preview(showBackground = true)
+@Composable
+private fun FilterDialogPreview() {
+ MainTheme { FilterDialog(8, 16.0, {}) { _, _ -> } }
+}
- @Inject
- lateinit var preferences: ISettingsManager
-
- override fun onCreateView(inflater: LayoutInflater, group: ViewGroup?, state: Bundle?): View? {
- return inflater.inflate(R.layout.dialog_filter, group, false)
- }
-
- override fun onViewCreated(view: View, state: Bundle?) {
- super.onViewCreated(view, state)
- DialogFilterBinding.bind(view).apply {
- dialog?.window?.setBackgroundDrawableResource(R.color.transparent)
- dialog?.window?.setLayout(
- (resources.displayMetrics.widthPixels * 0.94).toInt(),
- WindowManager.LayoutParams.WRAP_CONTENT
- )
- filterHoursEdit.setText(preferences.getHoursAhead().toString())
- filterElevEdit.setText(preferences.getMinElevation().toString())
- filterBtnPos.setOnClickListener {
- val hoursAhead = try {
- filterHoursEdit.text.toString().toInt()
- } catch (exception: Exception) {
- 24
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun FilterDialog(hours: Int, elevation: Double, toggle: () -> Unit, save: (Int, Double) -> Unit) {
+ val hoursValue = rememberSaveable { mutableStateOf(hours) }
+ val elevValue = rememberSaveable { mutableStateOf(elevation) }
+ Dialog(onDismissRequest = { toggle() }) {
+ ElevatedCard {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier.fillMaxWidth(1f)
+ ) {
+ Text(text = "Filter passes", color = MaterialTheme.colorScheme.primary)
+ Text(text = "Show passes that occur within X hours")
+ OutlinedTextField(value = hoursValue.value.toString(), onValueChange = { newValue ->
+ val hoursAhead = try {
+ newValue.toInt()
+ } catch (exception: Exception) {
+ 12
+ }
+ hoursValue.value = hoursAhead
+ })
+ Text(text = "Show passes with max elevation above")
+ OutlinedTextField(value = elevValue.value.toString(), onValueChange = { newValue ->
+ val maxElevation = try {
+ newValue.toDouble()
+ } catch (exception: Exception) {
+ 16.0
+ }
+ elevValue.value = maxElevation
+ })
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ CardButton(onClick = { toggle() }, text = "Cancel")
+ CardButton(
+ onClick = {
+ save(hoursValue.value, elevValue.value)
+ toggle()
+ }, text = "Accept"
+ )
}
- val minElevation = try {
- filterElevEdit.text.toString().toDouble()
- } catch (exception: Exception) {
- 16.0
- }
- setNavResult("prefs", Pair(hoursAhead, minElevation))
- dismiss()
}
- filterBtnNeg.setOnClickListener { dismiss() }
}
}
}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesAdapter.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesAdapter.kt
deleted file mode 100644
index 67a4f4a1..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesAdapter.kt
+++ /dev/null
@@ -1,124 +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 .
- */
-package com.rtbishop.look4sat.presentation.passesScreen
-
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.recyclerview.widget.AsyncListDiffer
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
-import com.rtbishop.look4sat.R
-import com.rtbishop.look4sat.databinding.ItemPassBinding
-import com.rtbishop.look4sat.domain.predict.SatPass
-import java.text.SimpleDateFormat
-import java.util.*
-
-class PassesAdapter(private val clickListener: PassesClickListener) :
- RecyclerView.Adapter() {
-
- private val diffCallback = object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
- return oldItem.catNum == newItem.catNum && oldItem.aosTime == newItem.aosTime
- }
-
- override fun areContentsTheSame(oldItem: SatPass, newItem: SatPass): Boolean {
- return oldItem.progress == newItem.progress
- }
- }
- private val differ = AsyncListDiffer(this, diffCallback)
- private var isUTC: Boolean = false
-
- interface PassesClickListener {
- fun navigateToPass(satPass: SatPass)
- }
-
- fun submitList(passes: List) {
- differ.submitList(passes)
- }
-
- fun setUTC(isUTC: Boolean) {
- this.isUTC = isUTC
- }
-
- override fun getItemCount() = differ.currentList.size
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
- return PassHolder.from(parent)
- }
-
- override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
- (holder as PassHolder).bind(differ.currentList[position], clickListener, isUTC)
- }
-
- class PassHolder private constructor(private val binding: ItemPassBinding) :
- RecyclerView.ViewHolder(binding.root) {
-
- private val satIdFormat = itemView.context.getString(R.string.pass_satId)
- private val aosAzFormat = itemView.context.getString(R.string.pass_aosAz)
- private val altFormat = itemView.context.getString(R.string.pass_altitude)
- private val losAzFormat = itemView.context.getString(R.string.pass_losAz)
- private val startTimeFormat = itemView.context.getString(R.string.pass_startTime)
- private val elevFormat = itemView.context.getString(R.string.pass_elevation)
- private val endTimeFormat = itemView.context.getString(R.string.pass_endTime)
- private val placeholder = itemView.context.getString(R.string.pass_placeholder)
- private val timeZoneUTC = TimeZone.getTimeZone("UTC")
- private val startFormat = SimpleDateFormat(startTimeFormat, Locale.getDefault())
- private val endFormat = SimpleDateFormat(endTimeFormat, Locale.getDefault())
-
- fun bind(satPass: SatPass, listener: PassesClickListener, shouldUseUTC: Boolean) {
- binding.apply {
- if (satPass.isDeepSpace) {
- passName.text = satPass.name
- passId.text = String.format(satIdFormat, satPass.catNum)
- passAos.text = String.format(aosAzFormat, satPass.aosAzimuth)
- passAltitude.text = String.format(altFormat, satPass.altitude)
- passLos.text = String.format(losAzFormat, satPass.losAzimuth)
- passStart.text = placeholder
- passElev.text = String.format(elevFormat, satPass.maxElevation)
- passEnd.text = placeholder
- passProgress.progress = 100
- } else {
- if (shouldUseUTC) {
- startFormat.timeZone = timeZoneUTC
- endFormat.timeZone = timeZoneUTC
- }
- passName.text = satPass.name
- passId.text = String.format(satIdFormat, satPass.catNum)
- passAos.text = String.format(aosAzFormat, satPass.aosAzimuth)
- passAltitude.text = String.format(altFormat, satPass.altitude)
- passLos.text = String.format(losAzFormat, satPass.losAzimuth)
- passStart.text = startFormat.format(Date(satPass.aosTime))
- passElev.text = String.format(elevFormat, satPass.maxElevation)
- passEnd.text = endFormat.format(Date(satPass.losTime))
- passProgress.progress = satPass.progress.toInt()
- }
- }
-
- itemView.setOnClickListener {
- listener.navigateToPass(satPass)
- }
- }
-
- companion object {
- fun from(parent: ViewGroup): PassHolder {
- val inflater = LayoutInflater.from(parent.context)
- return PassHolder(ItemPassBinding.inflate(inflater, parent, false))
- }
- }
- }
-}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt
deleted file mode 100644
index 41e99363..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesFragment.kt
+++ /dev/null
@@ -1,167 +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 .
- */
-package com.rtbishop.look4sat.presentation.passesScreen
-
-import android.os.Bundle
-import android.view.View
-import android.widget.Toast
-import androidx.fragment.app.Fragment
-import androidx.fragment.app.viewModels
-import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.SimpleItemAnimator
-import com.rtbishop.look4sat.R
-import com.rtbishop.look4sat.databinding.FragmentPassesBinding
-import com.rtbishop.look4sat.domain.model.DataState
-import com.rtbishop.look4sat.domain.predict.SatPass
-import com.rtbishop.look4sat.presentation.clickWithDebounce
-import com.rtbishop.look4sat.presentation.getNavResult
-import com.rtbishop.look4sat.utility.toTimerString
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesClickListener {
-
- private val viewModel: PassesViewModel by viewModels()
- private val passesAdapter = PassesAdapter(this)
- private var binding: FragmentPassesBinding? = null
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- binding = FragmentPassesBinding.bind(view).apply {
- passesAdapter.setUTC(viewModel.shouldUseUTC())
- passesRecycler.apply {
- setHasFixedSize(true)
- adapter = passesAdapter
- layoutManager = LinearLayoutManager(requireContext())
- (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
- addItemDecoration(DividerItemDecoration(requireContext(), 1))
- addOnScrollListener(object : RecyclerView.OnScrollListener() {
- override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
- if (dy > 0 && passesFab.visibility == View.VISIBLE) passesFab.hide()
- else if (dy < 0 && passesFab.visibility != View.VISIBLE) passesFab.show()
- }
- })
- }
- passesBtnRefresh.clickWithDebounce { viewModel.calculatePasses() }
- passesBtnMap.clickWithDebounce {
- val dir = PassesFragmentDirections.globalToMap()
- findNavController().navigate(dir)
- }
- passesBtnFilter.clickWithDebounce {
- val dir = PassesFragmentDirections.passesToFilter()
- findNavController().navigate(dir)
- }
- passesBtnSettings.clickWithDebounce {
- val dir = PassesFragmentDirections.globalToSettings()
- findNavController().navigate(dir)
- }
- setupObservers()
- }
- }
-
- override fun navigateToPass(satPass: SatPass) {
- if (satPass.progress < 100) satPass.run {
- val dir = PassesFragmentDirections.globalToRadar(this.catNum, this.aosTime)
- findNavController().navigate(dir)
- }
- }
-
- override fun onDestroyView() {
- binding?.passesRecycler?.adapter = null
- binding = null
- super.onDestroyView()
- }
-
- private fun setupObservers() {
- viewModel.entriesTotal.observe(viewLifecycleOwner) { number ->
- handleEntriesTotal(number)
- }
- viewModel.passes.observe(viewLifecycleOwner) { passesResult ->
- handleNewPasses(passesResult)
- }
- getNavResult>(R.id.nav_passes, "prefs") { prefs ->
- viewModel.calculatePasses(prefs.first, prefs.second)
- }
- getNavResult>(R.id.nav_passes, "selection") { items ->
- viewModel.calculatePasses(selection = items)
- }
- }
-
- private fun handleEntriesTotal(number: Int) {
- binding?.run {
- if (number > 0) {
- passesFab.clickWithDebounce {
- val direction = PassesFragmentDirections.globalToEntries()
- findNavController().navigate(direction)
- }
- } else {
- passesFab.clickWithDebounce {
- val errorMessage = getString(R.string.passes_empty_db)
- Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
- }
- }
- }
- }
-
- private fun handleNewPasses(state: DataState>) {
- binding?.run {
- when (state) {
- is DataState.Success -> {
- passesAdapter.submitList(state.data)
- tickMainTimer(state.data)
- if (state.data.isNotEmpty()) { // show new passes list
- passesEmpty.visibility = View.INVISIBLE
- passesProgress.visibility = View.INVISIBLE
- } else { // show no passes message
- passesEmpty.visibility = View.VISIBLE
- passesProgress.visibility = View.INVISIBLE
- }
- }
- is DataState.Loading -> {
- passesEmpty.visibility = View.INVISIBLE
- passesProgress.visibility = View.VISIBLE
- passesTimer.text = 0L.toTimerString()
- }
- else -> {}
- }
- }
- }
-
- private fun tickMainTimer(passes: List) {
- binding?.run {
- if (passes.isNotEmpty()) {
- val timeNow = System.currentTimeMillis()
- try {
- val nextPass = passes.first { it.aosTime.minus(timeNow) > 0 }
- val millisBeforeStart = nextPass.aosTime.minus(timeNow)
- passesTimer.text = millisBeforeStart.toTimerString()
- } catch (e: NoSuchElementException) {
- val lastPass = passes.last()
- val millisBeforeEnd = lastPass.losTime.minus(timeNow)
- passesTimer.text = millisBeforeEnd.toTimerString()
- }
- } else {
- passesTimer.text = 0L.toTimerString()
- }
- passesBtnRefresh.isEnabled = true
- }
- }
-}
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesScreen.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesScreen.kt
index 0f867ed2..aa0d56ae 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesScreen.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesScreen.kt
@@ -15,6 +15,8 @@ import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@@ -42,14 +44,23 @@ fun PassesScreen(viewModel: PassesViewModel = hiltViewModel()) {
val state = viewModel.passes.observeAsState()
val timerText = viewModel.timerText.observeAsState()
val isRefreshing = state.value is DataState.Loading
- val refreshState = rememberPullRefreshState(
- refreshing = isRefreshing,
+ val refreshState = rememberPullRefreshState(refreshing = isRefreshing,
onRefresh = { viewModel.calculatePasses() })
val value = state.value
var passes = emptyList()
if (value is DataState.Success) passes = value.data
+ val showDialog = rememberSaveable { mutableStateOf(false) }
+ val toggleDialog = { showDialog.value = showDialog.value.not() }
+ if (showDialog.value) {
+ FilterDialog(viewModel.getHoursAhead(),
+ viewModel.getMinElevation(),
+ { toggleDialog() }) { hours, elev ->
+ viewModel.calculatePasses(hours, elev)
+ }
+ }
+
Column(modifier = Modifier.padding(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) {
ElevatedCard(modifier = Modifier.height(52.dp)) {
Row(
@@ -57,7 +68,7 @@ fun PassesScreen(viewModel: PassesViewModel = hiltViewModel()) {
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxSize()
) {
- IconButton(onClick = {}) {
+ IconButton(onClick = { toggleDialog() }) {
Icon(
painter = painterResource(id = R.drawable.ic_filter),
contentDescription = null
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
index acc5704d..21537f53 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/passesScreen/PassesViewModel.kt
@@ -43,6 +43,10 @@ class PassesViewModel @Inject constructor(
val passes: LiveData>> = _passes
val timerText: LiveData> = _timerText
+ fun getHoursAhead() = settings.getHoursAhead()
+
+ fun getMinElevation() = settings.getMinElevation()
+
init {
viewModelScope.launch {
satelliteManager.calculatedPasses.collect { passes ->
diff --git a/app/src/main/res/layout/dialog_filter.xml b/app/src/main/res/layout/dialog_filter.xml
deleted file mode 100644
index cb09370c..00000000
--- a/app/src/main/res/layout/dialog_filter.xml
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/dialog_sources.xml b/app/src/main/res/layout/dialog_sources.xml
deleted file mode 100644
index 7e91fe2f..00000000
--- a/app/src/main/res/layout/dialog_sources.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/fragment_passes.xml b/app/src/main/res/layout/fragment_passes.xml
deleted file mode 100644
index 7acecd3e..00000000
--- a/app/src/main/res/layout/fragment_passes.xml
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/item_pass.xml b/app/src/main/res/layout/item_pass.xml
deleted file mode 100644
index 29ca06dc..00000000
--- a/app/src/main/res/layout/item_pass.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index 8a2fc7b1..f9170691 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -3,19 +3,7 @@
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_passes">
-
-
-
+ app:startDestination="@id/nav_radar">
-